@caupulican/pi-adaptative 0.81.26 → 0.81.29

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 (286) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/cli/args.d.ts.map +1 -1
  3. package/dist/cli/args.js +3 -2
  4. package/dist/cli/args.js.map +1 -1
  5. package/dist/core/agent-session-services.d.ts +4 -0
  6. package/dist/core/agent-session-services.d.ts.map +1 -1
  7. package/dist/core/agent-session-services.js +19 -2
  8. package/dist/core/agent-session-services.js.map +1 -1
  9. package/dist/core/agent-session.d.ts +30 -12
  10. package/dist/core/agent-session.d.ts.map +1 -1
  11. package/dist/core/agent-session.js +128 -36
  12. package/dist/core/agent-session.js.map +1 -1
  13. package/dist/core/auth-storage.d.ts +6 -0
  14. package/dist/core/auth-storage.d.ts.map +1 -1
  15. package/dist/core/auth-storage.js +35 -0
  16. package/dist/core/auth-storage.js.map +1 -1
  17. package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
  18. package/dist/core/autonomy/bounded-completion.js +59 -19
  19. package/dist/core/autonomy/bounded-completion.js.map +1 -1
  20. package/dist/core/autonomy/contracts.d.ts +2 -0
  21. package/dist/core/autonomy/contracts.d.ts.map +1 -1
  22. package/dist/core/autonomy/contracts.js.map +1 -1
  23. package/dist/core/autonomy/lane-tool-surface.d.ts +31 -0
  24. package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -0
  25. package/dist/core/autonomy/lane-tool-surface.js +96 -0
  26. package/dist/core/autonomy/lane-tool-surface.js.map +1 -0
  27. package/dist/core/autonomy/lane-tracker.d.ts +5 -0
  28. package/dist/core/autonomy/lane-tracker.d.ts.map +1 -1
  29. package/dist/core/autonomy/lane-tracker.js +14 -3
  30. package/dist/core/autonomy/lane-tracker.js.map +1 -1
  31. package/dist/core/background-lane-controller.d.ts +28 -4
  32. package/dist/core/background-lane-controller.d.ts.map +1 -1
  33. package/dist/core/background-lane-controller.js +237 -28
  34. package/dist/core/background-lane-controller.js.map +1 -1
  35. package/dist/core/billing-failover-controller.d.ts.map +1 -1
  36. package/dist/core/billing-failover-controller.js +1 -1
  37. package/dist/core/billing-failover-controller.js.map +1 -1
  38. package/dist/core/context/current-work-memory.d.ts +9 -0
  39. package/dist/core/context/current-work-memory.d.ts.map +1 -0
  40. package/dist/core/context/current-work-memory.js +61 -0
  41. package/dist/core/context/current-work-memory.js.map +1 -0
  42. package/dist/core/context/file-store-memory-provider.d.ts +9 -0
  43. package/dist/core/context/file-store-memory-provider.d.ts.map +1 -0
  44. package/dist/core/context/file-store-memory-provider.js +106 -0
  45. package/dist/core/context/file-store-memory-provider.js.map +1 -0
  46. package/dist/core/context/long-term-memory-trigger.d.ts +15 -0
  47. package/dist/core/context/long-term-memory-trigger.d.ts.map +1 -0
  48. package/dist/core/context/long-term-memory-trigger.js +39 -0
  49. package/dist/core/context/long-term-memory-trigger.js.map +1 -0
  50. package/dist/core/context/memory-prompt-block.d.ts +3 -1
  51. package/dist/core/context/memory-prompt-block.d.ts.map +1 -1
  52. package/dist/core/context/memory-prompt-block.js +20 -3
  53. package/dist/core/context/memory-prompt-block.js.map +1 -1
  54. package/dist/core/context/memory-prompt-budget.d.ts +17 -0
  55. package/dist/core/context/memory-prompt-budget.d.ts.map +1 -0
  56. package/dist/core/context/memory-prompt-budget.js +64 -0
  57. package/dist/core/context/memory-prompt-budget.js.map +1 -0
  58. package/dist/core/context/memory-provider-contract.d.ts.map +1 -1
  59. package/dist/core/context/memory-provider-contract.js +2 -5
  60. package/dist/core/context/memory-provider-contract.js.map +1 -1
  61. package/dist/core/context/memory-tier-composer.d.ts +27 -0
  62. package/dist/core/context/memory-tier-composer.d.ts.map +1 -0
  63. package/dist/core/context/memory-tier-composer.js +79 -0
  64. package/dist/core/context/memory-tier-composer.js.map +1 -0
  65. package/dist/core/context-gc.d.ts.map +1 -1
  66. package/dist/core/context-gc.js +2 -0
  67. package/dist/core/context-gc.js.map +1 -1
  68. package/dist/core/context-pipeline.d.ts +5 -3
  69. package/dist/core/context-pipeline.d.ts.map +1 -1
  70. package/dist/core/context-pipeline.js +15 -5
  71. package/dist/core/context-pipeline.js.map +1 -1
  72. package/dist/core/cost/cost-summary.d.ts +3 -1
  73. package/dist/core/cost/cost-summary.d.ts.map +1 -1
  74. package/dist/core/cost/cost-summary.js +3 -3
  75. package/dist/core/cost/cost-summary.js.map +1 -1
  76. package/dist/core/cost-guard.d.ts +17 -7
  77. package/dist/core/cost-guard.d.ts.map +1 -1
  78. package/dist/core/cost-guard.js +33 -10
  79. package/dist/core/cost-guard.js.map +1 -1
  80. package/dist/core/default-tool-surface.d.ts +9 -0
  81. package/dist/core/default-tool-surface.d.ts.map +1 -0
  82. package/dist/core/default-tool-surface.js +18 -0
  83. package/dist/core/default-tool-surface.js.map +1 -0
  84. package/dist/core/delegation/worker-result.d.ts.map +1 -1
  85. package/dist/core/delegation/worker-result.js +3 -0
  86. package/dist/core/delegation/worker-result.js.map +1 -1
  87. package/dist/core/delegation/worker-runner.d.ts +11 -5
  88. package/dist/core/delegation/worker-runner.d.ts.map +1 -1
  89. package/dist/core/delegation/worker-runner.js +77 -16
  90. package/dist/core/delegation/worker-runner.js.map +1 -1
  91. package/dist/core/extensions/loader.d.ts.map +1 -1
  92. package/dist/core/extensions/loader.js +15 -0
  93. package/dist/core/extensions/loader.js.map +1 -1
  94. package/dist/core/extensions/runner.d.ts +2 -0
  95. package/dist/core/extensions/runner.d.ts.map +1 -1
  96. package/dist/core/extensions/runner.js +7 -0
  97. package/dist/core/extensions/runner.js.map +1 -1
  98. package/dist/core/extensions/types.d.ts +9 -0
  99. package/dist/core/extensions/types.d.ts.map +1 -1
  100. package/dist/core/extensions/types.js.map +1 -1
  101. package/dist/core/failure-corpus.d.ts.map +1 -1
  102. package/dist/core/failure-corpus.js +2 -4
  103. package/dist/core/failure-corpus.js.map +1 -1
  104. package/dist/core/footer-data-provider.d.ts.map +1 -1
  105. package/dist/core/footer-data-provider.js +9 -1
  106. package/dist/core/footer-data-provider.js.map +1 -1
  107. package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
  108. package/dist/core/goals/goal-continuation-prompt.js +4 -3
  109. package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
  110. package/dist/core/http-dispatcher.d.ts +12 -0
  111. package/dist/core/http-dispatcher.d.ts.map +1 -1
  112. package/dist/core/http-dispatcher.js +42 -4
  113. package/dist/core/http-dispatcher.js.map +1 -1
  114. package/dist/core/local-runtime-controller.d.ts +16 -0
  115. package/dist/core/local-runtime-controller.d.ts.map +1 -1
  116. package/dist/core/local-runtime-controller.js +107 -28
  117. package/dist/core/local-runtime-controller.js.map +1 -1
  118. package/dist/core/memory/memory-manager.d.ts +8 -3
  119. package/dist/core/memory/memory-manager.d.ts.map +1 -1
  120. package/dist/core/memory/memory-manager.js +27 -11
  121. package/dist/core/memory/memory-manager.js.map +1 -1
  122. package/dist/core/memory/memory-provider.d.ts +5 -1
  123. package/dist/core/memory/memory-provider.d.ts.map +1 -1
  124. package/dist/core/memory/memory-provider.js.map +1 -1
  125. package/dist/core/memory/providers/file-store.d.ts +3 -1
  126. package/dist/core/memory/providers/file-store.d.ts.map +1 -1
  127. package/dist/core/memory/providers/file-store.js +7 -2
  128. package/dist/core/memory/providers/file-store.js.map +1 -1
  129. package/dist/core/memory/providers/transcript-recall.d.ts +1 -0
  130. package/dist/core/memory/providers/transcript-recall.d.ts.map +1 -1
  131. package/dist/core/memory/providers/transcript-recall.js +4 -7
  132. package/dist/core/memory/providers/transcript-recall.js.map +1 -1
  133. package/dist/core/memory-controller.d.ts +36 -10
  134. package/dist/core/memory-controller.d.ts.map +1 -1
  135. package/dist/core/memory-controller.js +137 -21
  136. package/dist/core/memory-controller.js.map +1 -1
  137. package/dist/core/model-registry.d.ts +25 -0
  138. package/dist/core/model-registry.d.ts.map +1 -1
  139. package/dist/core/model-registry.js +91 -8
  140. package/dist/core/model-registry.js.map +1 -1
  141. package/dist/core/model-resolver.d.ts.map +1 -1
  142. package/dist/core/model-resolver.js +30 -11
  143. package/dist/core/model-resolver.js.map +1 -1
  144. package/dist/core/model-router-controller.d.ts.map +1 -1
  145. package/dist/core/model-router-controller.js +33 -29
  146. package/dist/core/model-router-controller.js.map +1 -1
  147. package/dist/core/model-selection-controller.d.ts +3 -0
  148. package/dist/core/model-selection-controller.d.ts.map +1 -1
  149. package/dist/core/model-selection-controller.js +8 -1
  150. package/dist/core/model-selection-controller.js.map +1 -1
  151. package/dist/core/models/local-runtime.d.ts +2 -0
  152. package/dist/core/models/local-runtime.d.ts.map +1 -1
  153. package/dist/core/models/local-runtime.js +10 -4
  154. package/dist/core/models/local-runtime.js.map +1 -1
  155. package/dist/core/models/perf-profile.d.ts.map +1 -1
  156. package/dist/core/models/perf-profile.js +95 -33
  157. package/dist/core/models/perf-profile.js.map +1 -1
  158. package/dist/core/models/runtime-arbiter.d.ts +11 -0
  159. package/dist/core/models/runtime-arbiter.d.ts.map +1 -1
  160. package/dist/core/models/runtime-arbiter.js +35 -13
  161. package/dist/core/models/runtime-arbiter.js.map +1 -1
  162. package/dist/core/profile-filter-controller.d.ts +11 -0
  163. package/dist/core/profile-filter-controller.d.ts.map +1 -1
  164. package/dist/core/profile-filter-controller.js +56 -11
  165. package/dist/core/profile-filter-controller.js.map +1 -1
  166. package/dist/core/profile-registry.d.ts +2 -2
  167. package/dist/core/profile-registry.d.ts.map +1 -1
  168. package/dist/core/profile-registry.js +58 -7
  169. package/dist/core/profile-registry.js.map +1 -1
  170. package/dist/core/reflection-controller.d.ts +9 -7
  171. package/dist/core/reflection-controller.d.ts.map +1 -1
  172. package/dist/core/reflection-controller.js +81 -12
  173. package/dist/core/reflection-controller.js.map +1 -1
  174. package/dist/core/research/research-runner.d.ts +2 -2
  175. package/dist/core/research/research-runner.d.ts.map +1 -1
  176. package/dist/core/research/research-runner.js +8 -6
  177. package/dist/core/research/research-runner.js.map +1 -1
  178. package/dist/core/resource-loader.d.ts +4 -2
  179. package/dist/core/resource-loader.d.ts.map +1 -1
  180. package/dist/core/resource-loader.js +11 -1
  181. package/dist/core/resource-loader.js.map +1 -1
  182. package/dist/core/runtime-builder.d.ts +30 -3
  183. package/dist/core/runtime-builder.d.ts.map +1 -1
  184. package/dist/core/runtime-builder.js +140 -40
  185. package/dist/core/runtime-builder.js.map +1 -1
  186. package/dist/core/sdk.d.ts +8 -7
  187. package/dist/core/sdk.d.ts.map +1 -1
  188. package/dist/core/sdk.js +38 -5
  189. package/dist/core/sdk.js.map +1 -1
  190. package/dist/core/security/secret-text.d.ts +3 -0
  191. package/dist/core/security/secret-text.d.ts.map +1 -0
  192. package/dist/core/security/secret-text.js +26 -0
  193. package/dist/core/security/secret-text.js.map +1 -0
  194. package/dist/core/settings-manager.d.ts +59 -17
  195. package/dist/core/settings-manager.d.ts.map +1 -1
  196. package/dist/core/settings-manager.js +300 -51
  197. package/dist/core/settings-manager.js.map +1 -1
  198. package/dist/core/skills.d.ts.map +1 -1
  199. package/dist/core/skills.js +1 -1
  200. package/dist/core/skills.js.map +1 -1
  201. package/dist/core/system-prompt-builder.d.ts +7 -0
  202. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  203. package/dist/core/system-prompt-builder.js +23 -1
  204. package/dist/core/system-prompt-builder.js.map +1 -1
  205. package/dist/core/tool-gate-controller.d.ts +3 -0
  206. package/dist/core/tool-gate-controller.d.ts.map +1 -1
  207. package/dist/core/tool-gate-controller.js +20 -15
  208. package/dist/core/tool-gate-controller.js.map +1 -1
  209. package/dist/core/tool-selection/expected-utility.d.ts +51 -0
  210. package/dist/core/tool-selection/expected-utility.d.ts.map +1 -0
  211. package/dist/core/tool-selection/expected-utility.js +85 -0
  212. package/dist/core/tool-selection/expected-utility.js.map +1 -0
  213. package/dist/core/tool-selection/tool-performance-store.d.ts +82 -0
  214. package/dist/core/tool-selection/tool-performance-store.d.ts.map +1 -0
  215. package/dist/core/tool-selection/tool-performance-store.js +249 -0
  216. package/dist/core/tool-selection/tool-performance-store.js.map +1 -0
  217. package/dist/core/tool-selection/tool-selection-controller.d.ts +41 -0
  218. package/dist/core/tool-selection/tool-selection-controller.d.ts.map +1 -0
  219. package/dist/core/tool-selection/tool-selection-controller.js +199 -0
  220. package/dist/core/tool-selection/tool-selection-controller.js.map +1 -0
  221. package/dist/core/tools/delegate-status.d.ts +9 -0
  222. package/dist/core/tools/delegate-status.d.ts.map +1 -0
  223. package/dist/core/tools/delegate-status.js +52 -0
  224. package/dist/core/tools/delegate-status.js.map +1 -0
  225. package/dist/core/tools/delegate.d.ts +10 -0
  226. package/dist/core/tools/delegate.d.ts.map +1 -1
  227. package/dist/core/tools/delegate.js +36 -13
  228. package/dist/core/tools/delegate.js.map +1 -1
  229. package/dist/core/tools/run-toolkit-script.d.ts +3 -0
  230. package/dist/core/tools/run-toolkit-script.d.ts.map +1 -1
  231. package/dist/core/tools/run-toolkit-script.js +17 -7
  232. package/dist/core/tools/run-toolkit-script.js.map +1 -1
  233. package/dist/main.d.ts.map +1 -1
  234. package/dist/main.js +2 -0
  235. package/dist/main.js.map +1 -1
  236. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  237. package/dist/modes/interactive/components/footer.js +8 -1
  238. package/dist/modes/interactive/components/footer.js.map +1 -1
  239. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  240. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  241. package/dist/modes/interactive/components/settings-selector.js +69 -29
  242. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  243. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  244. package/dist/modes/interactive/components/thinking-selector.js +2 -0
  245. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  246. package/dist/modes/interactive/config-backup.d.ts +7 -1
  247. package/dist/modes/interactive/config-backup.d.ts.map +1 -1
  248. package/dist/modes/interactive/config-backup.js +196 -32
  249. package/dist/modes/interactive/config-backup.js.map +1 -1
  250. package/dist/modes/interactive/interactive-mode.d.ts +1 -1
  251. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  252. package/dist/modes/interactive/interactive-mode.js +22 -28
  253. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  254. package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
  255. package/dist/modes/interactive/local-model-commands.js +2 -0
  256. package/dist/modes/interactive/local-model-commands.js.map +1 -1
  257. package/dist/modes/interactive/profile-menu-controller.d.ts +6 -4
  258. package/dist/modes/interactive/profile-menu-controller.d.ts.map +1 -1
  259. package/dist/modes/interactive/profile-menu-controller.js +248 -65
  260. package/dist/modes/interactive/profile-menu-controller.js.map +1 -1
  261. package/dist/modes/interactive/report-commands.d.ts.map +1 -1
  262. package/dist/modes/interactive/report-commands.js +8 -2
  263. package/dist/modes/interactive/report-commands.js.map +1 -1
  264. package/dist/modes/interactive/settings-selector-flow.d.ts.map +1 -1
  265. package/dist/modes/interactive/settings-selector-flow.js +8 -0
  266. package/dist/modes/interactive/settings-selector-flow.js.map +1 -1
  267. package/dist/modes/interactive/theme/theme-schema.json +1 -1
  268. package/dist/modes/interactive/theme/theme.d.ts +1 -1
  269. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  270. package/dist/modes/interactive/theme/theme.js +3 -1
  271. package/dist/modes/interactive/theme/theme.js.map +1 -1
  272. package/docs/extensions.md +32 -0
  273. package/docs/integration-sweep-builder-blueprint-2026-07-09.md +365 -0
  274. package/docs/integration-sweep-resume-2026-07-09.md +407 -0
  275. package/docs/resources.md +15 -7
  276. package/docs/settings.md +56 -5
  277. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  278. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  279. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  280. package/examples/extensions/preset.ts +2 -2
  281. package/examples/extensions/sandbox/package-lock.json +2 -2
  282. package/examples/extensions/sandbox/package.json +1 -1
  283. package/examples/extensions/with-deps/package-lock.json +2 -2
  284. package/examples/extensions/with-deps/package.json +1 -1
  285. package/npm-shrinkwrap.json +44 -35
  286. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"memory-controller.d.ts","sourceRoot":"","sources":["../../src/core/memory-controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAiB,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EAEN,KAAK,2BAA2B,EAChC,KAAK,0BAA0B,EAE/B,MAAM,iCAAiC,CAAC;AAMzC,OAAO,EAAE,KAAK,qBAAqB,EAA4B,MAAM,+BAA+B,CAAC;AAGrG,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAIlE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AA0B7D,MAAM,WAAW,oBAAoB;IACpC,mGAAmG;IACnG,kBAAkB,IAAI,eAAe,CAAC;IACtC,8EAA8E;IAC9E,YAAY,IAAI,MAAM,CAAC;IACvB,qFAAmF;IACnF,WAAW,IAAI,MAAM,CAAC;IACtB,yDAAyD;IACzD,MAAM,IAAI,MAAM,CAAC;IACjB,4DAA4D;IAC5D,YAAY,IAAI,MAAM,CAAC;IACvB,oFAAoF;IACpF,cAAc,IAAI,OAAO,CAAC;IAC1B,iGAAiG;IACjG,mBAAmB,IAAI,IAAI,CAAC;CAC5B;AAED,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,kBAAkB,CAAgD;IAC1E,OAAO,CAAC,4BAA4B,CAAgD;IACpF,OAAO,CAAC,kCAAkC,CAAsD;IAChG,0FAA0F;IAC1F,OAAO,CAAC,cAAc,CAAsC;IAC5D,qFAAqF;IACrF,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAA8B;IACpE,oGAAoG;IACpG,OAAO,CAAC,uBAAuB,CAAwB;IAEvD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAuB;IAE5C,YAAY,IAAI,EAAE,oBAAoB,EAErC;IAED,0GAA0G;IAC1G,gBAAgB,IAAI,aAAa,CAEhC;IAED;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IAIrB;;;;;OAKG;IACH,OAAO,CAAC,qBAAqB;IAK7B;;;;;;;;;;;OAWG;IACG,kBAAkB,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAwBjF;IAED,uFAAuF;IACvF,wBAAwB,IAAI,qBAAqB,CAEhD;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,8BAA8B,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,MAAM,EAAE,qBAAqB,GAAG,YAAY,EAAE,CAoFtG;IAED,kHAAkH;IAClH,8BAA8B,IAAI,2BAA2B,CAE5D;IAED;;;;;OAKG;IACH,yBAAyB,IAAI;QAC5B,SAAS,EAAE,0BAA0B,CAAC;QACtC,eAAe,EAAE,2BAA2B,CAAC;KAC7C,CAMA;IAED;;;;OAIG;IACH,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUzC;IAED,uGAAuG;IACvG,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAErF;IAED;;;;OAIG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CA8BhC;IAED,mGAAmG;IACnG,sBAAsB,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAIrD;IAED,0GAA0G;IAC1G,qBAAqB,IAAI,IAAI,CAE5B;CACD","sourcesContent":["/**\n * Memory controller: the session's plug-and-play memory subsystem — the read-only OKF retrieval\n * provider, the bounded prompt-evidence surfacing pilot, cross-session recall effectiveness, and the\n * live {@link MemoryManager} (bundled file-store + transcript-recall providers plus any extension\n * contributions).\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Owns the lazily-built OKF\n * provider, the latest retrieval/prompt-inclusion reports, the recreated-on-reload MemoryManager, the\n * recall {@link EffectivenessTracker}, and the extension-contributed pending providers. Everything\n * else it needs — settings, the current turn index, agent/workspace dirs, the session id, the\n * child-session flag, and the tool-registry refresh — is reached through narrow deps accessors rather\n * than the whole AgentSession.\n *\n * Context-transform boundary (deliberate): {@link runMemoryRetrieval} and\n * {@link maybeAppendMemoryEvidenceBlock} are invoked from the session's context transform as one-line\n * delegations. This controller deliberately imports no compaction/context-pipeline internals — it only\n * ever reads settings and builds the retrieval report + the bounded evidence block, so the transform\n * stays the single owner of the pass ordering.\n */\n\nimport { join } from \"node:path\";\nimport type { AgentMessage, CustomMessage } from \"@caupulican/pi-agent-core\";\nimport {\n\tdefaultMemoryPromptInclusionReport,\n\ttype MemoryPromptInclusionReport,\n\ttype MemoryRetrievalDiagnostics,\n\tsanitizeMemoryRetrievalReportForDiagnostics,\n} from \"./context/memory-diagnostics.ts\";\nimport { buildMemoryPromptBlock } from \"./context/memory-prompt-block.ts\";\nimport {\n\ttype MemoryProvider as ContextMemoryProvider,\n\tDEFAULT_LOCAL_MEMORY_EGRESS_POLICY,\n} from \"./context/memory-provider-contract.ts\";\nimport { type MemoryRetrievalReport, retrieveMemoryForContext } from \"./context/memory-retrieval.ts\";\nimport { createOkfMemoryProvider } from \"./context/okf-memory-provider.ts\";\nimport { EffectivenessTracker } from \"./memory/effectiveness-tracker.ts\";\nimport { MemoryManager } from \"./memory/memory-manager.ts\";\nimport type { MemoryProvider } from \"./memory/memory-provider.ts\";\nimport { FileStoreProvider } from \"./memory/providers/file-store.ts\";\nimport { TranscriptRecallProvider } from \"./memory/providers/transcript-recall.ts\";\nimport { wrapUntrustedText } from \"./security/untrusted-boundary.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\n/**\n * Text of the most recent user message, or \"\" if there is none (e.g. goal-continuation\n * turns with no new user input). An empty query degrades to zero memory-retrieval results\n * by construction (see memory-provider-contract.ts's score-on-empty-query-tokens rule) --\n * no special-casing needed here beyond returning \"\".\n */\nfunction latestUserMessageText(messages: AgentMessage[]): string {\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tconst message = messages[index];\n\t\tif (message.role !== \"user\") continue;\n\t\tif (typeof message.content === \"string\") return message.content;\n\t\tconst parts: string[] = [];\n\t\tfor (const part of message.content) {\n\t\t\tif (part.type === \"text\") parts.push(part.text);\n\t\t}\n\t\treturn parts.join(\"\\n\");\n\t}\n\treturn \"\";\n}\n\nfunction emptyMemoryRetrievalReport(maxResults: number): MemoryRetrievalReport {\n\treturn { request: { query: \"\", maxResults }, providerReports: [], results: [], contextItems: [] };\n}\n\nexport interface MemoryControllerDeps {\n\t/** Memory-retrieval + prompt-inclusion settings (the opt-in gates for retrieval and surfacing). */\n\tgetSettingsManager(): SettingsManager;\n\t/** Current turn index, stamped into a retrieval request's `createdAtTurn`. */\n\tgetTurnIndex(): number;\n\t/** Agent root — the durable OKF memory docs live under `<agentDir>/okf-memory`. */\n\tgetAgentDir(): string;\n\t/** Workspace root, passed to provider initialization. */\n\tgetCwd(): string;\n\t/** This session's id, passed to provider initialization. */\n\tgetSessionId(): string;\n\t/** Child sessions gate durable memory writes; passed to provider initialization. */\n\tisChildSession(): boolean;\n\t/** Re-derive the tool registry after (re)init so the newly-surfaced memory tools take effect. */\n\trefreshToolRegistry(): void;\n}\n\nexport class MemoryController {\n\tprivate _memoryOkfProvider: ContextMemoryProvider | undefined = undefined;\n\tprivate _latestMemoryRetrievalReport: MemoryRetrievalReport | undefined = undefined;\n\tprivate _latestMemoryPromptInclusionReport: MemoryPromptInclusionReport | undefined = undefined;\n\t/** Plug-and-play memory subsystem. Recreated on each (re)initialize so reload is safe. */\n\tprivate _memoryManager: MemoryManager = new MemoryManager();\n\t/** R4: tracks whether injected recall is actually used, to adapt the recall gate. */\n\tprivate readonly _effectivenessTracker = new EffectivenessTracker();\n\t/** Memory providers registered by extensions via pi.registerMemoryProvider, applied on (re)init. */\n\tprivate _pendingMemoryProviders: MemoryProvider[] = [];\n\n\tprivate readonly deps: MemoryControllerDeps;\n\n\tconstructor(deps: MemoryControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\t/** The live memory manager. Callers reach prefetch / tool-definitions / markers / shutdown through it. */\n\tgetMemoryManager(): MemoryManager {\n\t\treturn this._memoryManager;\n\t}\n\n\t/**\n\t * Fixed path for this slice's local Pi OKF memory documents, shared across sessions\n\t * under this agentDir (not session-scoped, unlike tool-artifacts/context-gc, since OKF\n\t * memory represents durable cross-session knowledge, not a per-session capture). Not\n\t * yet user-configurable -- see the memory-retrieval settings doc comment.\n\t */\n\tprivate _memoryOkfDir(): string {\n\t\treturn join(this.deps.getAgentDir(), \"okf-memory\");\n\t}\n\n\t/**\n\t * Session-scoped, read-only local OKF memory provider. Lazily created ONLY when memory\n\t * retrieval is enabled (see `runMemoryRetrieval`) -- never force-created, so a session\n\t * with the setting off never touches `_memoryOkfDir()` at all (no directory access, no\n\t * creation; `createOkfMemoryProvider` itself never writes/mkdirs either way).\n\t */\n\tprivate _getMemoryOkfProvider(): ContextMemoryProvider {\n\t\tthis._memoryOkfProvider ??= createOkfMemoryProvider({ rootDir: this._memoryOkfDir() });\n\t\treturn this._memoryOkfProvider;\n\t}\n\n\t/**\n\t * Observe-only local memory retrieval (see context/memory-retrieval.ts and\n\t * context/okf-memory-provider.ts): default disabled, opt-in setting. When disabled,\n\t * never constructs the OKF provider (no directory access under `_memoryOkfDir()` at\n\t * all) and returns an empty report -- fully fail-closed. When enabled, queries the\n\t * local, read-only OKF provider with the latest user message text (empty if there is\n\t * none, e.g. a goal-continuation turn -- degrades to zero results by construction, see\n\t * `latestUserMessageText`'s doc comment) under `DEFAULT_LOCAL_MEMORY_EGRESS_POLICY`.\n\t * Retrieved items are only ever stored in the report; nothing here touches `messages`,\n\t * the transcript, or the provider-visible prompt. Never throws into a live turn: any\n\t * failure (including a provider search error) degrades to an empty report.\n\t */\n\tasync runMemoryRetrieval(messages: AgentMessage[]): Promise<MemoryRetrievalReport> {\n\t\ttry {\n\t\t\tconst settings = this.deps.getSettingsManager().getMemoryRetrievalSettings();\n\t\t\tif (!settings.enabled) {\n\t\t\t\tconst report = emptyMemoryRetrievalReport(settings.maxResults);\n\t\t\t\tthis._latestMemoryRetrievalReport = report;\n\t\t\t\treturn report;\n\t\t\t}\n\t\t\tconst report = await retrieveMemoryForContext(\n\t\t\t\t[this._getMemoryOkfProvider()],\n\t\t\t\t{ query: latestUserMessageText(messages), maxResults: settings.maxResults },\n\t\t\t\t{\n\t\t\t\t\tcreatedAtTurn: this.deps.getTurnIndex(),\n\t\t\t\t\tmaxResults: settings.maxResults,\n\t\t\t\t\tdefaultLocalPolicy: DEFAULT_LOCAL_MEMORY_EGRESS_POLICY,\n\t\t\t\t},\n\t\t\t);\n\t\t\tthis._latestMemoryRetrievalReport = report;\n\t\t\treturn report;\n\t\t} catch {\n\t\t\tconst report = emptyMemoryRetrievalReport(0);\n\t\t\tthis._latestMemoryRetrievalReport = report;\n\t\t\treturn report;\n\t\t}\n\t}\n\n\t/** Read-only inspection of the latest memory-retrieval report, for tests/debugging. */\n\tgetMemoryRetrievalReport(): MemoryRetrievalReport {\n\t\treturn this._latestMemoryRetrievalReport ?? emptyMemoryRetrievalReport(0);\n\t}\n\n\t/**\n\t * Bounded prompt-surfacing pilot for local memory evidence (see\n\t * context/memory-prompt-block.ts): opt-in, default disabled, and gated on TWO settings\n\t * (`enabled` AND `includeInPrompt`) plus a non-empty `report.contextItems` -- the first\n\t * two are belt-and-suspenders on top of the fact that `runMemoryRetrieval` already\n\t * leaves `contextItems` empty whenever `enabled` is false, regardless of\n\t * `includeInPrompt`. Reuses the `report` this pass's `runMemoryRetrieval` call already\n\t * computed -- never re-queries the provider here.\n\t *\n\t * Appends exactly one ephemeral `custom`/\"memory_evidence\" message wrapped by\n\t * `wrapUntrustedText` (the same nonce-fenced boundary + always-on system-prompt rule\n\t * used for other untrusted content) to the END of `messages`. This is purely additive\n\t * (never mutates an existing message) and purely transient: `messages` here is the\n\t * array about to be sent to the provider, not `this.agent.state.messages` or anything\n\t * persisted via `sessionManager` -- so the injected message can never reach the\n\t * transcript, regardless of how many times this pass runs.\n\t *\n\t * Also records a `MemoryPromptInclusionReport` (context/memory-diagnostics.ts) at each\n\t * branch below, for context_audit's diagnostic surface only -- this is pure bookkeeping\n\t * alongside the existing branches, not a new branch/condition: the messages returned\n\t * are unchanged by this recording.\n\t */\n\tmaybeAppendMemoryEvidenceBlock(messages: AgentMessage[], report: MemoryRetrievalReport): AgentMessage[] {\n\t\ttry {\n\t\t\tconst settings = this.deps.getSettingsManager().getMemoryRetrievalSettings();\n\t\t\tconst base = {\n\t\t\t\tenabled: settings.enabled,\n\t\t\t\tincludeInPrompt: settings.includeInPrompt,\n\t\t\t\tselectedItemCount: report.contextItems.length,\n\t\t\t};\n\t\t\tif (!settings.enabled) {\n\t\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\t\t...base,\n\t\t\t\t\tstatus: \"disabled\",\n\t\t\t\t\tincludedCount: 0,\n\t\t\t\t\tomittedCount: 0,\n\t\t\t\t\tblockChars: 0,\n\t\t\t\t};\n\t\t\t\treturn messages;\n\t\t\t}\n\t\t\tif (!settings.includeInPrompt) {\n\t\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\t\t...base,\n\t\t\t\t\tstatus: \"include_disabled\",\n\t\t\t\t\tincludedCount: 0,\n\t\t\t\t\tomittedCount: 0,\n\t\t\t\t\tblockChars: 0,\n\t\t\t\t};\n\t\t\t\treturn messages;\n\t\t\t}\n\t\t\tif (report.contextItems.length === 0) {\n\t\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\t\t...base,\n\t\t\t\t\tstatus: \"no_results\",\n\t\t\t\t\tincludedCount: 0,\n\t\t\t\t\tomittedCount: 0,\n\t\t\t\t\tblockChars: 0,\n\t\t\t\t};\n\t\t\t\treturn messages;\n\t\t\t}\n\n\t\t\tconst block = buildMemoryPromptBlock(report.contextItems);\n\t\t\tif (!block.text) {\n\t\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\t\t...base,\n\t\t\t\t\tstatus: \"empty_block\",\n\t\t\t\t\tincludedCount: block.includedCount,\n\t\t\t\t\tomittedCount: block.omittedCount,\n\t\t\t\t\tblockChars: 0,\n\t\t\t\t};\n\t\t\t\treturn messages;\n\t\t\t}\n\n\t\t\tconst wrapped = wrapUntrustedText(block.text, \"memory:pi-okf\");\n\t\t\tconst evidenceMessage: CustomMessage = {\n\t\t\t\trole: \"custom\",\n\t\t\t\tcustomType: \"memory_evidence\",\n\t\t\t\tcontent: [{ type: \"text\", text: wrapped }],\n\t\t\t\tdisplay: false,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t};\n\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\t...base,\n\t\t\t\tstatus: \"included\",\n\t\t\t\tincludedCount: block.includedCount,\n\t\t\t\tomittedCount: block.omittedCount,\n\t\t\t\tblockChars: wrapped.length,\n\t\t\t\tsourceLabel: \"memory:pi-okf\",\n\t\t\t};\n\t\t\treturn [...messages, evidenceMessage];\n\t\t} catch {\n\t\t\t// `base` may not exist yet if the throw happened before it was computed (e.g.\n\t\t\t// settings access or `report.contextItems` itself threw), so this branch cannot\n\t\t\t// rely on it -- fall back to safe, fixed defaults rather than risk referencing\n\t\t\t// a partially-evaluated value.\n\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\tenabled: false,\n\t\t\t\tincludeInPrompt: false,\n\t\t\t\tselectedItemCount: 0,\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tincludedCount: 0,\n\t\t\t\tomittedCount: 0,\n\t\t\t\tblockChars: 0,\n\t\t\t};\n\t\t\treturn messages;\n\t\t}\n\t}\n\n\t/** Read-only inspection of the latest memory-prompt-inclusion decision, for tests/debugging and context_audit. */\n\tgetMemoryPromptInclusionReport(): MemoryPromptInclusionReport {\n\t\treturn this._latestMemoryPromptInclusionReport ?? defaultMemoryPromptInclusionReport();\n\t}\n\n\t/**\n\t * Combines the already-stored, no-arg latest reports (never re-queries the provider or\n\t * touches the OKF directory) into the safe, allow-list-projected shape context_audit\n\t * exposes. See context/memory-diagnostics.ts for why this projection is allow-list\n\t * based rather than a spread-then-delete of the raw report.\n\t */\n\tgetMemoryAuditDiagnostics(): {\n\t\tretrieval: MemoryRetrievalDiagnostics;\n\t\tpromptInclusion: MemoryPromptInclusionReport;\n\t} {\n\t\tconst settings = this.deps.getSettingsManager().getMemoryRetrievalSettings();\n\t\treturn {\n\t\t\tretrieval: sanitizeMemoryRetrievalReportForDiagnostics(this.getMemoryRetrievalReport(), settings),\n\t\t\tpromptInclusion: this.getMemoryPromptInclusionReport(),\n\t\t};\n\t}\n\n\t/**\n\t * Zero-I/O gate for cross-session recall (R3): skip trivial turns (short acks, slash commands) so\n\t * recall only runs when it could plausibly help. The provider's similarity cutoff is the real\n\t * filter — this just avoids the index query on turns that obviously don't warrant it.\n\t */\n\tshouldAttemptRecall(text: string): boolean {\n\t\tconst t = text.trim();\n\t\tif (t.length < 12 || t.startsWith(\"/\")) return false;\n\t\tconst words = t.split(/\\s+/).filter((w) => w.length >= 3);\n\t\t// R4 adaptive gate: if recall has rarely been used lately (enough samples to trust the signal),\n\t\t// raise the bar so we only recall on clearly substantial turns — and relax it again once recall\n\t\t// starts paying off. Never fully disabled, so the loop can recover.\n\t\tconst recallRarelyUseful =\n\t\t\tthis._effectivenessTracker.sampleCount >= 5 && this._effectivenessTracker.usefulLately() < 0.15;\n\t\treturn words.length >= (recallRarelyUseful ? 6 : 3);\n\t}\n\n\t/** R4: score whether the agent actually used an injected recall page, so the recall gate can adapt. */\n\trecordRecallOutcome(recallText: string, queryText: string, responseText: string): void {\n\t\tthis._effectivenessTracker.recordRecallOutcome(recallText, queryText, responseText);\n\t}\n\n\t/**\n\t * (Re)build the memory subsystem: a fresh MemoryManager (reload-safe), register the bundled\n\t * file-store + any extension-contributed providers, initialize, then surface the memory tools and\n\t * the frozen system-prompt block. Best-effort: never throws into the session lifecycle.\n\t */\n\tasync initialize(): Promise<void> {\n\t\ttry {\n\t\t\t// Release the previous generation's providers (locks/handles) before recreating, so a\n\t\t\t// reload does not orphan the old MemoryManager. No-op on first init / for file-store.\n\t\t\tawait this._memoryManager.shutdownAll().catch(() => {});\n\t\t\tconst manager = new MemoryManager();\n\t\t\tmanager.registerProvider(new FileStoreProvider());\n\t\t\t// Bundled read-only cross-session recall (R3): indexes past-session transcripts and answers\n\t\t\t// prefetch() with a <memory_context> page. Never writes.\n\t\t\tmanager.registerProvider(new TranscriptRecallProvider());\n\t\t\tfor (const provider of this._pendingMemoryProviders) {\n\t\t\t\ttry {\n\t\t\t\t\tmanager.registerProvider(provider);\n\t\t\t\t} catch {\n\t\t\t\t\t// Duplicate name or reserved-tool collision — skip this provider, keep the rest.\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis._memoryManager = manager;\n\t\t\tawait manager.initializeAll(this.deps.getSessionId(), {\n\t\t\t\tagentDir: this.deps.getAgentDir(),\n\t\t\t\tcwd: this.deps.getCwd(),\n\t\t\t\tisChildSession: this.deps.isChildSession(),\n\t\t\t});\n\t\t\t// Surface memory tools + the frozen memory block now that providers are initialized.\n\t\t\t// refreshToolRegistry() ends in setActiveToolsByName(), which rebuilds AND assigns the\n\t\t\t// system prompt (including the memory block), so no explicit _rebuildSystemPrompt is needed.\n\t\t\tthis.deps.refreshToolRegistry();\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Memory subsystem init failed:\", error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n\n\t/** Register a memory provider contributed by an extension; applied on the next memory (re)init. */\n\tregisterMemoryProvider(provider: MemoryProvider): void {\n\t\tif (!this._pendingMemoryProviders.some((p) => p.name === provider.name)) {\n\t\t\tthis._pendingMemoryProviders.push(provider);\n\t\t}\n\t}\n\n\t/** Reload starts memory providers fresh; loaded extensions re-register before the next `initialize()`. */\n\tclearPendingProviders(): void {\n\t\tthis._pendingMemoryProviders = [];\n\t}\n}\n"]}
1
+ {"version":3,"file":"memory-controller.d.ts","sourceRoot":"","sources":["../../src/core/memory-controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Memory controller: the session's plug-and-play memory subsystem — the read-only OKF retrieval\n * provider, the bounded prompt-evidence surfacing pilot, cross-session recall effectiveness, and the\n * live {@link MemoryManager} (bundled file-store + transcript-recall providers plus any extension\n * contributions).\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Owns the lazily-built OKF\n * provider, the latest retrieval/prompt-inclusion reports, the recreated-on-reload MemoryManager, the\n * recall {@link EffectivenessTracker}, and the extension-contributed pending providers. Everything\n * else it needs — settings, the current turn index, agent/workspace dirs, the session id, the\n * child-session flag, and the tool-registry refresh — is reached through narrow deps accessors rather\n * than the whole AgentSession.\n *\n * Context-transform boundary (deliberate): {@link runMemoryRetrieval} and\n * {@link maybeAppendMemoryEvidenceBlock} are invoked from the session's context transform as one-line\n * delegations. This controller deliberately imports no compaction/context-pipeline internals — it only\n * ever reads settings and builds the retrieval report + the bounded evidence block, so the transform\n * stays the single owner of the pass ordering.\n */\n\nimport { join } from \"node:path\";\nimport type { AgentMessage, CustomMessage } from \"@caupulican/pi-agent-core\";\nimport { collectCurrentWorkMemory } from \"./context/current-work-memory.ts\";\nimport { createFileStoreMemoryProvider } from \"./context/file-store-memory-provider.ts\";\nimport { shouldQueryLongTermMemory } from \"./context/long-term-memory-trigger.ts\";\nimport {\n\tdefaultMemoryPromptInclusionReport,\n\ttype MemoryPromptInclusionReport,\n\ttype MemoryRetrievalDiagnostics,\n\tsanitizeMemoryRetrievalReportForDiagnostics,\n} from \"./context/memory-diagnostics.ts\";\nimport { type MemoryPromptBudget, resolveMemoryPromptBudget } from \"./context/memory-prompt-budget.ts\";\nimport {\n\ttype MemoryProvider as ContextMemoryProvider,\n\tDEFAULT_EXTERNAL_MEMORY_EGRESS_POLICY,\n\tDEFAULT_LOCAL_MEMORY_EGRESS_POLICY,\n} from \"./context/memory-provider-contract.ts\";\nimport { type MemoryRetrievalReport, retrieveMemoryForContext } from \"./context/memory-retrieval.ts\";\nimport { composeTieredMemoryPromptBlock, type MemoryTierCandidate } from \"./context/memory-tier-composer.ts\";\nimport { createOkfMemoryProvider } from \"./context/okf-memory-provider.ts\";\nimport type { GoalState } from \"./goals/goal-state.ts\";\nimport { EffectivenessTracker } from \"./memory/effectiveness-tracker.ts\";\nimport { MemoryManager } from \"./memory/memory-manager.ts\";\nimport type { MemoryProvider } from \"./memory/memory-provider.ts\";\nimport { FileStoreProvider } from \"./memory/providers/file-store.ts\";\nimport { TranscriptRecallProvider } from \"./memory/providers/transcript-recall.ts\";\nimport { wrapUntrustedText } from \"./security/untrusted-boundary.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\n/**\n * Text of the most recent user message, or \"\" if there is none (e.g. goal-continuation\n * turns with no new user input). An empty query is still valid: standing user preferences may\n * surface from the file-store retrieval fallback when the static memory prompt cannot fit,\n * while long-term providers remain gated by shouldQueryLongTermMemory().\n */\nfunction latestUserMessageText(messages: AgentMessage[]): string {\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tconst message = messages[index];\n\t\tif (message.role !== \"user\") continue;\n\t\tif (typeof message.content === \"string\") return message.content;\n\t\tconst parts: string[] = [];\n\t\tfor (const part of message.content) {\n\t\t\tif (part.type === \"text\") parts.push(part.text);\n\t\t}\n\t\treturn parts.join(\"\\n\");\n\t}\n\treturn \"\";\n}\n\nfunction emptyMemoryRetrievalReport(maxResults: number): MemoryRetrievalReport {\n\treturn { request: { query: \"\", maxResults }, providerReports: [], results: [], contextItems: [] };\n}\n\nconst ENABLED_EXTERNAL_MEMORY_EGRESS_POLICY = {\n\t...DEFAULT_EXTERNAL_MEMORY_EGRESS_POLICY,\n\tenabled: true,\n\tallowedScopes: DEFAULT_LOCAL_MEMORY_EGRESS_POLICY.allowedScopes,\n\tallowExternalEgress: true,\n} as const;\n\nexport interface MemoryControllerDeps {\n\t/** Memory-retrieval + prompt-inclusion settings (default-on gates for retrieval and surfacing). */\n\tgetSettingsManager(): SettingsManager;\n\t/** Current turn index, stamped into a retrieval request's `createdAtTurn`. */\n\tgetTurnIndex(): number;\n\t/** Agent root — the durable OKF memory docs live under `<agentDir>/okf-memory`. */\n\tgetAgentDir(): string;\n\t/** Workspace root, passed to provider initialization. */\n\tgetCwd(): string;\n\t/** This session's id, passed to provider initialization. */\n\tgetSessionId(): string;\n\t/** Child sessions gate durable memory writes; passed to provider initialization. */\n\tisChildSession(): boolean;\n\t/** Re-derive the tool registry after (re)init so the newly-surfaced memory tools take effect. */\n\trefreshToolRegistry(): void;\n\t/** Active model context window, used to cap prompt-visible memory. */\n\tgetContextWindow(): number | undefined;\n\t/** Latest active goal state, used for short-term current-work memory. */\n\tgetGoalState(): GoalState | undefined;\n}\n\n/** Extension-contributed memory state staged across an atomic runtime reload. */\nexport interface MemoryControllerReloadSnapshot {\n\tpendingMemoryProviders: MemoryProvider[];\n\tpendingContextMemoryProviders: ContextMemoryProvider[];\n\tmemoryOkfProvider: ContextMemoryProvider | undefined;\n\tfileStoreMemoryProvider: ContextMemoryProvider | undefined;\n}\n\nexport class MemoryController {\n\tprivate _memoryOkfProvider: ContextMemoryProvider | undefined = undefined;\n\tprivate _fileStoreMemoryProvider: ContextMemoryProvider | undefined = undefined;\n\tprivate _latestMemoryRetrievalReport: MemoryRetrievalReport | undefined = undefined;\n\tprivate _latestMemoryPromptInclusionReport: MemoryPromptInclusionReport | undefined = undefined;\n\t/** Plug-and-play memory subsystem. Recreated on each (re)initialize so reload is safe. */\n\tprivate _memoryManager: MemoryManager = new MemoryManager();\n\t/** R4: tracks whether injected recall is actually used, to adapt the recall gate. */\n\tprivate readonly _effectivenessTracker = new EffectivenessTracker();\n\t/** Memory providers registered by extensions via pi.registerMemoryProvider, applied on (re)init. */\n\tprivate _pendingMemoryProviders: MemoryProvider[] = [];\n\t/** Context-memory providers registered by extensions via pi.registerContextMemoryProvider. */\n\tprivate _pendingContextMemoryProviders: ContextMemoryProvider[] = [];\n\n\tprivate readonly deps: MemoryControllerDeps;\n\n\tconstructor(deps: MemoryControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\t/** The live memory manager. Callers reach prefetch / tool-definitions / markers / shutdown through it. */\n\tgetMemoryManager(): MemoryManager {\n\t\treturn this._memoryManager;\n\t}\n\n\t/**\n\t * Fixed path for this slice's local Pi OKF memory documents, shared across sessions\n\t * under this agentDir (not session-scoped, unlike tool-artifacts/context-gc, since OKF\n\t * memory represents durable cross-session knowledge, not a per-session capture). Not\n\t * yet user-configurable -- see the memory-retrieval settings doc comment.\n\t */\n\tprivate _memoryOkfDir(): string {\n\t\treturn join(this.deps.getAgentDir(), \"okf-memory\");\n\t}\n\n\t/**\n\t * Session-scoped, read-only local OKF memory provider. Lazily created ONLY when memory\n\t * retrieval is enabled (see `runMemoryRetrieval`) -- never force-created, so a session\n\t * with the setting off never touches `_memoryOkfDir()` at all (no directory access, no\n\t * creation; `createOkfMemoryProvider` itself never writes/mkdirs either way).\n\t */\n\tprivate _getMemoryOkfProvider(): ContextMemoryProvider {\n\t\tthis._memoryOkfProvider ??= createOkfMemoryProvider({ rootDir: this._memoryOkfDir() });\n\t\treturn this._memoryOkfProvider;\n\t}\n\n\tprivate _getFileStoreMemoryProvider(): ContextMemoryProvider {\n\t\tthis._fileStoreMemoryProvider ??= createFileStoreMemoryProvider({\n\t\t\tmemoryFilePath: join(this.deps.getAgentDir(), \"MEMORY.md\"),\n\t\t\tuserFilePath: join(this.deps.getAgentDir(), \"USER.md\"),\n\t\t});\n\t\treturn this._fileStoreMemoryProvider;\n\t}\n\n\tprivate _memoryBudget(configuredMaxResults: number) {\n\t\treturn resolveMemoryPromptBudget({\n\t\t\tcontextWindow: this.deps.getContextWindow(),\n\t\t\tconfiguredMaxResults,\n\t\t});\n\t}\n\n\tprivate _shouldQueryFileStoreFallback(budget: MemoryPromptBudget): boolean {\n\t\t// MEMORY.md/USER.md are already injected through the static file-store prompt on\n\t\t// normal windows, so querying them again would duplicate provider-visible memory.\n\t\t// In compact windows the static block can be omitted because it does not fit; only\n\t\t// then use the retrieval view to surface a few budget-pruned lines.\n\t\tif (!budget.enabled || !budget.compact) return false;\n\t\treturn this._memoryManager.buildSystemPromptBlock(budget).trim().length === 0;\n\t}\n\n\t/**\n\t * Observe-only local memory retrieval (see context/memory-retrieval.ts and\n\t * context/okf-memory-provider.ts): default-on, but settings-gated. When disabled,\n\t * never constructs built-in context-memory providers (no directory access under\n\t * `_memoryOkfDir()` at all) and returns an empty report -- fully fail-closed. When enabled,\n\t * queries local, read-only providers with the latest user message text (empty if there is\n\t * none, e.g. a goal-continuation turn) under `DEFAULT_LOCAL_MEMORY_EGRESS_POLICY`.\n\t * Retrieved items are only ever stored in the report; nothing here touches `messages`,\n\t * the transcript, or the provider-visible prompt. Never throws into a live turn: any\n\t * failure (including a provider search error) degrades to an empty report.\n\t */\n\tasync runMemoryRetrieval(messages: AgentMessage[]): Promise<MemoryRetrievalReport> {\n\t\ttry {\n\t\t\tconst settings = this.deps.getSettingsManager().getMemoryRetrievalSettings();\n\t\t\tif (!settings.enabled) {\n\t\t\t\tconst report = emptyMemoryRetrievalReport(settings.maxResults);\n\t\t\t\tthis._latestMemoryRetrievalReport = report;\n\t\t\t\treturn report;\n\t\t\t}\n\t\t\tconst query = latestUserMessageText(messages);\n\t\t\tconst budget = this._memoryBudget(settings.maxResults);\n\t\t\tconst currentWork = collectCurrentWorkMemory({ goalState: this.deps.getGoalState() });\n\t\t\tconst longTermDecision = shouldQueryLongTermMemory({\n\t\t\t\tlatestUserText: query,\n\t\t\t\tgoalState: this.deps.getGoalState(),\n\t\t\t\tbudget,\n\t\t\t\tcurrentWorkCandidateCount: currentWork.length,\n\t\t\t});\n\t\t\tconst providers = this._shouldQueryFileStoreFallback(budget) ? [this._getFileStoreMemoryProvider()] : [];\n\t\t\tif (longTermDecision.shouldQuery) {\n\t\t\t\tproviders.push(this._getMemoryOkfProvider(), ...this._pendingContextMemoryProviders);\n\t\t\t}\n\t\t\tconst maxResults = budget.enabled ? Math.min(settings.maxResults, budget.maxResults) : settings.maxResults;\n\t\t\tconst report = await retrieveMemoryForContext(\n\t\t\t\tproviders,\n\t\t\t\t{ query, maxResults },\n\t\t\t\t{\n\t\t\t\t\tcreatedAtTurn: this.deps.getTurnIndex(),\n\t\t\t\t\tmaxResults,\n\t\t\t\t\tdefaultLocalPolicy: DEFAULT_LOCAL_MEMORY_EGRESS_POLICY,\n\t\t\t\t\tdefaultExternalPolicy: settings.allowExternalEgress\n\t\t\t\t\t\t? ENABLED_EXTERNAL_MEMORY_EGRESS_POLICY\n\t\t\t\t\t\t: DEFAULT_EXTERNAL_MEMORY_EGRESS_POLICY,\n\t\t\t\t},\n\t\t\t);\n\t\t\tthis._latestMemoryRetrievalReport = report;\n\t\t\treturn report;\n\t\t} catch {\n\t\t\tconst report = emptyMemoryRetrievalReport(0);\n\t\t\tthis._latestMemoryRetrievalReport = report;\n\t\t\treturn report;\n\t\t}\n\t}\n\n\t/** Read-only inspection of the latest memory-retrieval report, for tests/debugging. */\n\tgetMemoryRetrievalReport(): MemoryRetrievalReport {\n\t\treturn this._latestMemoryRetrievalReport ?? emptyMemoryRetrievalReport(0);\n\t}\n\n\tprivate _candidateForContextItem(\n\t\titem: MemoryRetrievalReport[\"contextItems\"][number],\n\t): MemoryTierCandidate | undefined {\n\t\tconst summary = item.summary?.trim();\n\t\tif (!summary) return undefined;\n\t\tconst ref = item.primaryRef?.type === \"memory\" ? item.primaryRef.ref : undefined;\n\t\tconst sourceLabel =\n\t\t\tref?.providerId === \"pi-file-store\" && ref.kind === \"user_preference\"\n\t\t\t\t? \"rule:user\"\n\t\t\t\t: `memory:${ref?.providerId ?? item.source}`;\n\t\tconst tier = ref?.kind === \"user_preference\" ? \"standing\" : \"long_term\";\n\t\treturn {\n\t\t\tid: item.id,\n\t\t\ttier,\n\t\t\tsourceLabel,\n\t\t\tsummary,\n\t\t\tscore: 0.5,\n\t\t\tevidenceRefs: item.evidenceRefs,\n\t\t};\n\t}\n\n\tprivate _memoryCandidates(report: MemoryRetrievalReport): MemoryTierCandidate[] {\n\t\treturn [\n\t\t\t...collectCurrentWorkMemory({ goalState: this.deps.getGoalState() }),\n\t\t\t...report.contextItems.flatMap((item) => {\n\t\t\t\tconst candidate = this._candidateForContextItem(item);\n\t\t\t\treturn candidate === undefined ? [] : [candidate];\n\t\t\t}),\n\t\t];\n\t}\n\n\t/**\n\t * Bounded prompt-surfacing for local memory evidence (see context/memory-tier-composer.ts):\n\t * default-on, but gated on TWO settings (`enabled` AND `includeInPrompt`) plus at least one\n\t * current-work, standing, or retrieved memory candidate -- the first\n\t * two are belt-and-suspenders on top of the fact that `runMemoryRetrieval` already\n\t * leaves `contextItems` empty whenever `enabled` is false, regardless of\n\t * `includeInPrompt`. Reuses the `report` this pass's `runMemoryRetrieval` call already\n\t * computed -- never re-queries the provider here.\n\t *\n\t * Appends exactly one ephemeral `custom`/\"memory_evidence\" message wrapped by\n\t * `wrapUntrustedText` (the same nonce-fenced boundary + always-on system-prompt rule\n\t * used for other untrusted content) to the END of `messages`. This is purely additive\n\t * (never mutates an existing message) and purely transient: `messages` here is the\n\t * array about to be sent to the provider, not `this.agent.state.messages` or anything\n\t * persisted via `sessionManager` -- so the injected message can never reach the\n\t * transcript, regardless of how many times this pass runs.\n\t *\n\t * Also records a `MemoryPromptInclusionReport` (context/memory-diagnostics.ts) at each\n\t * branch below, for context_audit's diagnostic surface only -- this is pure bookkeeping\n\t * alongside the existing branches, not a new branch/condition: the messages returned\n\t * are unchanged by this recording.\n\t */\n\tmaybeAppendMemoryEvidenceBlock(messages: AgentMessage[], report: MemoryRetrievalReport): AgentMessage[] {\n\t\ttry {\n\t\t\tconst settings = this.deps.getSettingsManager().getMemoryRetrievalSettings();\n\t\t\tconst candidates = this._memoryCandidates(report);\n\t\t\tconst base = {\n\t\t\t\tenabled: settings.enabled,\n\t\t\t\tincludeInPrompt: settings.includeInPrompt,\n\t\t\t\tselectedItemCount: candidates.length,\n\t\t\t};\n\t\t\tif (!settings.enabled) {\n\t\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\t\t...base,\n\t\t\t\t\tstatus: \"disabled\",\n\t\t\t\t\tincludedCount: 0,\n\t\t\t\t\tomittedCount: 0,\n\t\t\t\t\tblockChars: 0,\n\t\t\t\t};\n\t\t\t\treturn messages;\n\t\t\t}\n\t\t\tif (!settings.includeInPrompt) {\n\t\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\t\t...base,\n\t\t\t\t\tstatus: \"include_disabled\",\n\t\t\t\t\tincludedCount: 0,\n\t\t\t\t\tomittedCount: 0,\n\t\t\t\t\tblockChars: 0,\n\t\t\t\t};\n\t\t\t\treturn messages;\n\t\t\t}\n\t\t\tif (candidates.length === 0) {\n\t\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\t\t...base,\n\t\t\t\t\tstatus: \"no_results\",\n\t\t\t\t\tincludedCount: 0,\n\t\t\t\t\tomittedCount: 0,\n\t\t\t\t\tblockChars: 0,\n\t\t\t\t};\n\t\t\t\treturn messages;\n\t\t\t}\n\n\t\t\tconst budget = this._memoryBudget(settings.maxResults);\n\t\t\tconst block = composeTieredMemoryPromptBlock(candidates, budget);\n\t\t\tif (!block.text) {\n\t\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\t\t...base,\n\t\t\t\t\tstatus: \"empty_block\",\n\t\t\t\t\tincludedCount: block.includedCount,\n\t\t\t\t\tomittedCount: block.omittedCount,\n\t\t\t\t\tblockChars: 0,\n\t\t\t\t};\n\t\t\t\treturn messages;\n\t\t\t}\n\n\t\t\tconst wrapped = wrapUntrustedText(block.text, \"memory:tiered\");\n\t\t\tconst evidenceMessage: CustomMessage = {\n\t\t\t\trole: \"custom\",\n\t\t\t\tcustomType: \"memory_evidence\",\n\t\t\t\tcontent: [{ type: \"text\", text: wrapped }],\n\t\t\t\tdisplay: false,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t};\n\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\t...base,\n\t\t\t\tstatus: \"included\",\n\t\t\t\tincludedCount: block.includedCount,\n\t\t\t\tomittedCount: block.omittedCount,\n\t\t\t\tblockChars: wrapped.length,\n\t\t\t\tsourceLabel: \"memory:tiered\",\n\t\t\t};\n\t\t\treturn [...messages, evidenceMessage];\n\t\t} catch {\n\t\t\t// `base` may not exist yet if the throw happened before it was computed (e.g.\n\t\t\t// settings access or `report.contextItems` itself threw), so this branch cannot\n\t\t\t// rely on it -- fall back to safe, fixed defaults rather than risk referencing\n\t\t\t// a partially-evaluated value.\n\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\tenabled: false,\n\t\t\t\tincludeInPrompt: false,\n\t\t\t\tselectedItemCount: 0,\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tincludedCount: 0,\n\t\t\t\tomittedCount: 0,\n\t\t\t\tblockChars: 0,\n\t\t\t};\n\t\t\treturn messages;\n\t\t}\n\t}\n\n\t/** Read-only inspection of the latest memory-prompt-inclusion decision, for tests/debugging and context_audit. */\n\tgetMemoryPromptInclusionReport(): MemoryPromptInclusionReport {\n\t\treturn this._latestMemoryPromptInclusionReport ?? defaultMemoryPromptInclusionReport();\n\t}\n\n\t/**\n\t * Combines the already-stored, no-arg latest reports (never re-queries the provider or\n\t * touches the OKF directory) into the safe, allow-list-projected shape context_audit\n\t * exposes. See context/memory-diagnostics.ts for why this projection is allow-list\n\t * based rather than a spread-then-delete of the raw report.\n\t */\n\tgetMemoryAuditDiagnostics(): {\n\t\tretrieval: MemoryRetrievalDiagnostics;\n\t\tpromptInclusion: MemoryPromptInclusionReport;\n\t} {\n\t\tconst settings = this.deps.getSettingsManager().getMemoryRetrievalSettings();\n\t\treturn {\n\t\t\tretrieval: sanitizeMemoryRetrievalReportForDiagnostics(this.getMemoryRetrievalReport(), settings),\n\t\t\tpromptInclusion: this.getMemoryPromptInclusionReport(),\n\t\t};\n\t}\n\n\t/**\n\t * Zero-I/O gate for cross-session recall (R3): skip trivial turns (short acks, slash commands) so\n\t * recall only runs when it could plausibly help. The provider's similarity cutoff is the real\n\t * filter — this just avoids the index query on turns that obviously don't warrant it.\n\t */\n\tshouldAttemptRecall(text: string): boolean {\n\t\tif (!this.deps.getSettingsManager().getMemoryRetrievalSettings().enabled) return false;\n\t\tconst t = text.trim();\n\t\tif (t.length < 12 || t.startsWith(\"/\")) return false;\n\t\tconst words = t.split(/\\s+/).filter((w) => w.length >= 3);\n\t\t// R4 adaptive gate: if recall has rarely been used lately (enough samples to trust the signal),\n\t\t// raise the bar so we only recall on clearly substantial turns — and relax it again once recall\n\t\t// starts paying off. Never fully disabled, so the loop can recover.\n\t\tconst recallRarelyUseful =\n\t\t\tthis._effectivenessTracker.sampleCount >= 5 && this._effectivenessTracker.usefulLately() < 0.15;\n\t\treturn words.length >= (recallRarelyUseful ? 6 : 3);\n\t}\n\n\t/** Legacy recall prefetch with the same hard-off and explicit external-egress policy as context retrieval. */\n\tasync prefetchRecall(query: string): Promise<string> {\n\t\tconst settings = this.deps.getSettingsManager().getMemoryRetrievalSettings();\n\t\tif (!settings.enabled) return \"\";\n\t\treturn this._memoryManager.prefetch(query, {\n\t\t\texternalEgressPolicy: settings.allowExternalEgress\n\t\t\t\t? ENABLED_EXTERNAL_MEMORY_EGRESS_POLICY\n\t\t\t\t: DEFAULT_EXTERNAL_MEMORY_EGRESS_POLICY,\n\t\t});\n\t}\n\n\t/** R4: score whether the agent actually used an injected recall page, so the recall gate can adapt. */\n\trecordRecallOutcome(recallText: string, queryText: string, responseText: string): void {\n\t\tthis._effectivenessTracker.recordRecallOutcome(recallText, queryText, responseText);\n\t}\n\n\t/**\n\t * (Re)build the memory subsystem: a fresh MemoryManager (reload-safe), register the bundled\n\t * file-store + any extension-contributed providers, initialize, then surface the memory tools and\n\t * the frozen system-prompt block. Best-effort: never throws into the session lifecycle.\n\t */\n\tasync initialize(): Promise<void> {\n\t\ttry {\n\t\t\t// Release the previous generation's providers (locks/handles) before recreating, so a\n\t\t\t// reload does not orphan the old MemoryManager. No-op on first init / for file-store.\n\t\t\tawait this._memoryManager.shutdownAll().catch(() => {});\n\t\t\tconst manager = new MemoryManager();\n\t\t\tmanager.registerProvider(new FileStoreProvider());\n\t\t\t// Bundled read-only cross-session recall (R3): indexes past-session transcripts and answers\n\t\t\t// prefetch() with a <memory_context> page. Never writes.\n\t\t\tmanager.registerProvider(new TranscriptRecallProvider());\n\t\t\tfor (const provider of this._pendingMemoryProviders) {\n\t\t\t\ttry {\n\t\t\t\t\tmanager.registerProvider(provider);\n\t\t\t\t} catch {\n\t\t\t\t\t// Duplicate name or reserved-tool collision — skip this provider, keep the rest.\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis._memoryManager = manager;\n\t\t\tawait manager.initializeAll(this.deps.getSessionId(), {\n\t\t\t\tagentDir: this.deps.getAgentDir(),\n\t\t\t\tcwd: this.deps.getCwd(),\n\t\t\t\tisChildSession: this.deps.isChildSession(),\n\t\t\t});\n\t\t\t// Surface memory tools + the frozen memory block now that providers are initialized.\n\t\t\t// refreshToolRegistry() ends in setActiveToolsByName(), which rebuilds AND assigns the\n\t\t\t// system prompt (including the memory block), so no explicit _rebuildSystemPrompt is needed.\n\t\t\tthis.deps.refreshToolRegistry();\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Memory subsystem init failed:\", error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n\n\t/** Register a memory provider contributed by an extension; applied on the next memory (re)init. */\n\tregisterMemoryProvider(provider: MemoryProvider): void {\n\t\tif (!this._pendingMemoryProviders.some((p) => p.name === provider.name)) {\n\t\t\tthis._pendingMemoryProviders.push(provider);\n\t\t}\n\t}\n\n\t/** Register a retrieval-style context memory provider contributed by an extension. */\n\tregisterContextMemoryProvider(provider: ContextMemoryProvider): void {\n\t\tif (!this._pendingContextMemoryProviders.some((p) => p.id === provider.id)) {\n\t\t\tthis._pendingContextMemoryProviders.push(provider);\n\t\t}\n\t}\n\n\tcreateReloadSnapshot(): MemoryControllerReloadSnapshot {\n\t\treturn {\n\t\t\tpendingMemoryProviders: [...this._pendingMemoryProviders],\n\t\t\tpendingContextMemoryProviders: [...this._pendingContextMemoryProviders],\n\t\t\tmemoryOkfProvider: this._memoryOkfProvider,\n\t\t\tfileStoreMemoryProvider: this._fileStoreMemoryProvider,\n\t\t};\n\t}\n\n\trestoreReloadSnapshot(snapshot: MemoryControllerReloadSnapshot): void {\n\t\tthis._pendingMemoryProviders = [...snapshot.pendingMemoryProviders];\n\t\tthis._pendingContextMemoryProviders = [...snapshot.pendingContextMemoryProviders];\n\t\tthis._memoryOkfProvider = snapshot.memoryOkfProvider;\n\t\tthis._fileStoreMemoryProvider = snapshot.fileStoreMemoryProvider;\n\t}\n\n\t/** Reload starts memory providers fresh; loaded extensions re-register before the next `initialize()`. */\n\tclearPendingProviders(): void {\n\t\tthis._pendingMemoryProviders = [];\n\t\tthis._pendingContextMemoryProviders = [];\n\t\tthis._memoryOkfProvider = undefined;\n\t\tthis._fileStoreMemoryProvider = undefined;\n\t}\n}\n"]}
@@ -18,10 +18,14 @@
18
18
  * stays the single owner of the pass ordering.
19
19
  */
20
20
  import { join } from "node:path";
21
+ import { collectCurrentWorkMemory } from "./context/current-work-memory.js";
22
+ import { createFileStoreMemoryProvider } from "./context/file-store-memory-provider.js";
23
+ import { shouldQueryLongTermMemory } from "./context/long-term-memory-trigger.js";
21
24
  import { defaultMemoryPromptInclusionReport, sanitizeMemoryRetrievalReportForDiagnostics, } from "./context/memory-diagnostics.js";
22
- import { buildMemoryPromptBlock } from "./context/memory-prompt-block.js";
23
- import { DEFAULT_LOCAL_MEMORY_EGRESS_POLICY, } from "./context/memory-provider-contract.js";
25
+ import { resolveMemoryPromptBudget } from "./context/memory-prompt-budget.js";
26
+ import { DEFAULT_EXTERNAL_MEMORY_EGRESS_POLICY, DEFAULT_LOCAL_MEMORY_EGRESS_POLICY, } from "./context/memory-provider-contract.js";
24
27
  import { retrieveMemoryForContext } from "./context/memory-retrieval.js";
28
+ import { composeTieredMemoryPromptBlock } from "./context/memory-tier-composer.js";
25
29
  import { createOkfMemoryProvider } from "./context/okf-memory-provider.js";
26
30
  import { EffectivenessTracker } from "./memory/effectiveness-tracker.js";
27
31
  import { MemoryManager } from "./memory/memory-manager.js";
@@ -30,9 +34,9 @@ import { TranscriptRecallProvider } from "./memory/providers/transcript-recall.j
30
34
  import { wrapUntrustedText } from "./security/untrusted-boundary.js";
31
35
  /**
32
36
  * Text of the most recent user message, or "" if there is none (e.g. goal-continuation
33
- * turns with no new user input). An empty query degrades to zero memory-retrieval results
34
- * by construction (see memory-provider-contract.ts's score-on-empty-query-tokens rule) --
35
- * no special-casing needed here beyond returning "".
37
+ * turns with no new user input). An empty query is still valid: standing user preferences may
38
+ * surface from the file-store retrieval fallback when the static memory prompt cannot fit,
39
+ * while long-term providers remain gated by shouldQueryLongTermMemory().
36
40
  */
37
41
  function latestUserMessageText(messages) {
38
42
  for (let index = messages.length - 1; index >= 0; index--) {
@@ -53,8 +57,15 @@ function latestUserMessageText(messages) {
53
57
  function emptyMemoryRetrievalReport(maxResults) {
54
58
  return { request: { query: "", maxResults }, providerReports: [], results: [], contextItems: [] };
55
59
  }
60
+ const ENABLED_EXTERNAL_MEMORY_EGRESS_POLICY = {
61
+ ...DEFAULT_EXTERNAL_MEMORY_EGRESS_POLICY,
62
+ enabled: true,
63
+ allowedScopes: DEFAULT_LOCAL_MEMORY_EGRESS_POLICY.allowedScopes,
64
+ allowExternalEgress: true,
65
+ };
56
66
  export class MemoryController {
57
67
  _memoryOkfProvider = undefined;
68
+ _fileStoreMemoryProvider = undefined;
58
69
  _latestMemoryRetrievalReport = undefined;
59
70
  _latestMemoryPromptInclusionReport = undefined;
60
71
  /** Plug-and-play memory subsystem. Recreated on each (re)initialize so reload is safe. */
@@ -63,6 +74,8 @@ export class MemoryController {
63
74
  _effectivenessTracker = new EffectivenessTracker();
64
75
  /** Memory providers registered by extensions via pi.registerMemoryProvider, applied on (re)init. */
65
76
  _pendingMemoryProviders = [];
77
+ /** Context-memory providers registered by extensions via pi.registerContextMemoryProvider. */
78
+ _pendingContextMemoryProviders = [];
66
79
  deps;
67
80
  constructor(deps) {
68
81
  this.deps = deps;
@@ -90,14 +103,35 @@ export class MemoryController {
90
103
  this._memoryOkfProvider ??= createOkfMemoryProvider({ rootDir: this._memoryOkfDir() });
91
104
  return this._memoryOkfProvider;
92
105
  }
106
+ _getFileStoreMemoryProvider() {
107
+ this._fileStoreMemoryProvider ??= createFileStoreMemoryProvider({
108
+ memoryFilePath: join(this.deps.getAgentDir(), "MEMORY.md"),
109
+ userFilePath: join(this.deps.getAgentDir(), "USER.md"),
110
+ });
111
+ return this._fileStoreMemoryProvider;
112
+ }
113
+ _memoryBudget(configuredMaxResults) {
114
+ return resolveMemoryPromptBudget({
115
+ contextWindow: this.deps.getContextWindow(),
116
+ configuredMaxResults,
117
+ });
118
+ }
119
+ _shouldQueryFileStoreFallback(budget) {
120
+ // MEMORY.md/USER.md are already injected through the static file-store prompt on
121
+ // normal windows, so querying them again would duplicate provider-visible memory.
122
+ // In compact windows the static block can be omitted because it does not fit; only
123
+ // then use the retrieval view to surface a few budget-pruned lines.
124
+ if (!budget.enabled || !budget.compact)
125
+ return false;
126
+ return this._memoryManager.buildSystemPromptBlock(budget).trim().length === 0;
127
+ }
93
128
  /**
94
129
  * Observe-only local memory retrieval (see context/memory-retrieval.ts and
95
- * context/okf-memory-provider.ts): default disabled, opt-in setting. When disabled,
96
- * never constructs the OKF provider (no directory access under `_memoryOkfDir()` at
97
- * all) and returns an empty report -- fully fail-closed. When enabled, queries the
98
- * local, read-only OKF provider with the latest user message text (empty if there is
99
- * none, e.g. a goal-continuation turn -- degrades to zero results by construction, see
100
- * `latestUserMessageText`'s doc comment) under `DEFAULT_LOCAL_MEMORY_EGRESS_POLICY`.
130
+ * context/okf-memory-provider.ts): default-on, but settings-gated. When disabled,
131
+ * never constructs built-in context-memory providers (no directory access under
132
+ * `_memoryOkfDir()` at all) and returns an empty report -- fully fail-closed. When enabled,
133
+ * queries local, read-only providers with the latest user message text (empty if there is
134
+ * none, e.g. a goal-continuation turn) under `DEFAULT_LOCAL_MEMORY_EGRESS_POLICY`.
101
135
  * Retrieved items are only ever stored in the report; nothing here touches `messages`,
102
136
  * the transcript, or the provider-visible prompt. Never throws into a live turn: any
103
137
  * failure (including a provider search error) degrades to an empty report.
@@ -110,10 +144,27 @@ export class MemoryController {
110
144
  this._latestMemoryRetrievalReport = report;
111
145
  return report;
112
146
  }
113
- const report = await retrieveMemoryForContext([this._getMemoryOkfProvider()], { query: latestUserMessageText(messages), maxResults: settings.maxResults }, {
147
+ const query = latestUserMessageText(messages);
148
+ const budget = this._memoryBudget(settings.maxResults);
149
+ const currentWork = collectCurrentWorkMemory({ goalState: this.deps.getGoalState() });
150
+ const longTermDecision = shouldQueryLongTermMemory({
151
+ latestUserText: query,
152
+ goalState: this.deps.getGoalState(),
153
+ budget,
154
+ currentWorkCandidateCount: currentWork.length,
155
+ });
156
+ const providers = this._shouldQueryFileStoreFallback(budget) ? [this._getFileStoreMemoryProvider()] : [];
157
+ if (longTermDecision.shouldQuery) {
158
+ providers.push(this._getMemoryOkfProvider(), ...this._pendingContextMemoryProviders);
159
+ }
160
+ const maxResults = budget.enabled ? Math.min(settings.maxResults, budget.maxResults) : settings.maxResults;
161
+ const report = await retrieveMemoryForContext(providers, { query, maxResults }, {
114
162
  createdAtTurn: this.deps.getTurnIndex(),
115
- maxResults: settings.maxResults,
163
+ maxResults,
116
164
  defaultLocalPolicy: DEFAULT_LOCAL_MEMORY_EGRESS_POLICY,
165
+ defaultExternalPolicy: settings.allowExternalEgress
166
+ ? ENABLED_EXTERNAL_MEMORY_EGRESS_POLICY
167
+ : DEFAULT_EXTERNAL_MEMORY_EGRESS_POLICY,
117
168
  });
118
169
  this._latestMemoryRetrievalReport = report;
119
170
  return report;
@@ -128,10 +179,37 @@ export class MemoryController {
128
179
  getMemoryRetrievalReport() {
129
180
  return this._latestMemoryRetrievalReport ?? emptyMemoryRetrievalReport(0);
130
181
  }
182
+ _candidateForContextItem(item) {
183
+ const summary = item.summary?.trim();
184
+ if (!summary)
185
+ return undefined;
186
+ const ref = item.primaryRef?.type === "memory" ? item.primaryRef.ref : undefined;
187
+ const sourceLabel = ref?.providerId === "pi-file-store" && ref.kind === "user_preference"
188
+ ? "rule:user"
189
+ : `memory:${ref?.providerId ?? item.source}`;
190
+ const tier = ref?.kind === "user_preference" ? "standing" : "long_term";
191
+ return {
192
+ id: item.id,
193
+ tier,
194
+ sourceLabel,
195
+ summary,
196
+ score: 0.5,
197
+ evidenceRefs: item.evidenceRefs,
198
+ };
199
+ }
200
+ _memoryCandidates(report) {
201
+ return [
202
+ ...collectCurrentWorkMemory({ goalState: this.deps.getGoalState() }),
203
+ ...report.contextItems.flatMap((item) => {
204
+ const candidate = this._candidateForContextItem(item);
205
+ return candidate === undefined ? [] : [candidate];
206
+ }),
207
+ ];
208
+ }
131
209
  /**
132
- * Bounded prompt-surfacing pilot for local memory evidence (see
133
- * context/memory-prompt-block.ts): opt-in, default disabled, and gated on TWO settings
134
- * (`enabled` AND `includeInPrompt`) plus a non-empty `report.contextItems` -- the first
210
+ * Bounded prompt-surfacing for local memory evidence (see context/memory-tier-composer.ts):
211
+ * default-on, but gated on TWO settings (`enabled` AND `includeInPrompt`) plus at least one
212
+ * current-work, standing, or retrieved memory candidate -- the first
135
213
  * two are belt-and-suspenders on top of the fact that `runMemoryRetrieval` already
136
214
  * leaves `contextItems` empty whenever `enabled` is false, regardless of
137
215
  * `includeInPrompt`. Reuses the `report` this pass's `runMemoryRetrieval` call already
@@ -153,10 +231,11 @@ export class MemoryController {
153
231
  maybeAppendMemoryEvidenceBlock(messages, report) {
154
232
  try {
155
233
  const settings = this.deps.getSettingsManager().getMemoryRetrievalSettings();
234
+ const candidates = this._memoryCandidates(report);
156
235
  const base = {
157
236
  enabled: settings.enabled,
158
237
  includeInPrompt: settings.includeInPrompt,
159
- selectedItemCount: report.contextItems.length,
238
+ selectedItemCount: candidates.length,
160
239
  };
161
240
  if (!settings.enabled) {
162
241
  this._latestMemoryPromptInclusionReport = {
@@ -178,7 +257,7 @@ export class MemoryController {
178
257
  };
179
258
  return messages;
180
259
  }
181
- if (report.contextItems.length === 0) {
260
+ if (candidates.length === 0) {
182
261
  this._latestMemoryPromptInclusionReport = {
183
262
  ...base,
184
263
  status: "no_results",
@@ -188,7 +267,8 @@ export class MemoryController {
188
267
  };
189
268
  return messages;
190
269
  }
191
- const block = buildMemoryPromptBlock(report.contextItems);
270
+ const budget = this._memoryBudget(settings.maxResults);
271
+ const block = composeTieredMemoryPromptBlock(candidates, budget);
192
272
  if (!block.text) {
193
273
  this._latestMemoryPromptInclusionReport = {
194
274
  ...base,
@@ -199,7 +279,7 @@ export class MemoryController {
199
279
  };
200
280
  return messages;
201
281
  }
202
- const wrapped = wrapUntrustedText(block.text, "memory:pi-okf");
282
+ const wrapped = wrapUntrustedText(block.text, "memory:tiered");
203
283
  const evidenceMessage = {
204
284
  role: "custom",
205
285
  customType: "memory_evidence",
@@ -213,7 +293,7 @@ export class MemoryController {
213
293
  includedCount: block.includedCount,
214
294
  omittedCount: block.omittedCount,
215
295
  blockChars: wrapped.length,
216
- sourceLabel: "memory:pi-okf",
296
+ sourceLabel: "memory:tiered",
217
297
  };
218
298
  return [...messages, evidenceMessage];
219
299
  }
@@ -257,6 +337,8 @@ export class MemoryController {
257
337
  * filter — this just avoids the index query on turns that obviously don't warrant it.
258
338
  */
259
339
  shouldAttemptRecall(text) {
340
+ if (!this.deps.getSettingsManager().getMemoryRetrievalSettings().enabled)
341
+ return false;
260
342
  const t = text.trim();
261
343
  if (t.length < 12 || t.startsWith("/"))
262
344
  return false;
@@ -267,6 +349,17 @@ export class MemoryController {
267
349
  const recallRarelyUseful = this._effectivenessTracker.sampleCount >= 5 && this._effectivenessTracker.usefulLately() < 0.15;
268
350
  return words.length >= (recallRarelyUseful ? 6 : 3);
269
351
  }
352
+ /** Legacy recall prefetch with the same hard-off and explicit external-egress policy as context retrieval. */
353
+ async prefetchRecall(query) {
354
+ const settings = this.deps.getSettingsManager().getMemoryRetrievalSettings();
355
+ if (!settings.enabled)
356
+ return "";
357
+ return this._memoryManager.prefetch(query, {
358
+ externalEgressPolicy: settings.allowExternalEgress
359
+ ? ENABLED_EXTERNAL_MEMORY_EGRESS_POLICY
360
+ : DEFAULT_EXTERNAL_MEMORY_EGRESS_POLICY,
361
+ });
362
+ }
270
363
  /** R4: score whether the agent actually used an injected recall page, so the recall gate can adapt. */
271
364
  recordRecallOutcome(recallText, queryText, responseText) {
272
365
  this._effectivenessTracker.recordRecallOutcome(recallText, queryText, responseText);
@@ -315,9 +408,32 @@ export class MemoryController {
315
408
  this._pendingMemoryProviders.push(provider);
316
409
  }
317
410
  }
411
+ /** Register a retrieval-style context memory provider contributed by an extension. */
412
+ registerContextMemoryProvider(provider) {
413
+ if (!this._pendingContextMemoryProviders.some((p) => p.id === provider.id)) {
414
+ this._pendingContextMemoryProviders.push(provider);
415
+ }
416
+ }
417
+ createReloadSnapshot() {
418
+ return {
419
+ pendingMemoryProviders: [...this._pendingMemoryProviders],
420
+ pendingContextMemoryProviders: [...this._pendingContextMemoryProviders],
421
+ memoryOkfProvider: this._memoryOkfProvider,
422
+ fileStoreMemoryProvider: this._fileStoreMemoryProvider,
423
+ };
424
+ }
425
+ restoreReloadSnapshot(snapshot) {
426
+ this._pendingMemoryProviders = [...snapshot.pendingMemoryProviders];
427
+ this._pendingContextMemoryProviders = [...snapshot.pendingContextMemoryProviders];
428
+ this._memoryOkfProvider = snapshot.memoryOkfProvider;
429
+ this._fileStoreMemoryProvider = snapshot.fileStoreMemoryProvider;
430
+ }
318
431
  /** Reload starts memory providers fresh; loaded extensions re-register before the next `initialize()`. */
319
432
  clearPendingProviders() {
320
433
  this._pendingMemoryProviders = [];
434
+ this._pendingContextMemoryProviders = [];
435
+ this._memoryOkfProvider = undefined;
436
+ this._fileStoreMemoryProvider = undefined;
321
437
  }
322
438
  }
323
439
  //# sourceMappingURL=memory-controller.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"memory-controller.js","sourceRoot":"","sources":["../../src/core/memory-controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Memory controller: the session's plug-and-play memory subsystem — the read-only OKF retrieval\n * provider, the bounded prompt-evidence surfacing pilot, cross-session recall effectiveness, and the\n * live {@link MemoryManager} (bundled file-store + transcript-recall providers plus any extension\n * contributions).\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Owns the lazily-built OKF\n * provider, the latest retrieval/prompt-inclusion reports, the recreated-on-reload MemoryManager, the\n * recall {@link EffectivenessTracker}, and the extension-contributed pending providers. Everything\n * else it needs — settings, the current turn index, agent/workspace dirs, the session id, the\n * child-session flag, and the tool-registry refresh — is reached through narrow deps accessors rather\n * than the whole AgentSession.\n *\n * Context-transform boundary (deliberate): {@link runMemoryRetrieval} and\n * {@link maybeAppendMemoryEvidenceBlock} are invoked from the session's context transform as one-line\n * delegations. This controller deliberately imports no compaction/context-pipeline internals — it only\n * ever reads settings and builds the retrieval report + the bounded evidence block, so the transform\n * stays the single owner of the pass ordering.\n */\n\nimport { join } from \"node:path\";\nimport type { AgentMessage, CustomMessage } from \"@caupulican/pi-agent-core\";\nimport {\n\tdefaultMemoryPromptInclusionReport,\n\ttype MemoryPromptInclusionReport,\n\ttype MemoryRetrievalDiagnostics,\n\tsanitizeMemoryRetrievalReportForDiagnostics,\n} from \"./context/memory-diagnostics.ts\";\nimport { buildMemoryPromptBlock } from \"./context/memory-prompt-block.ts\";\nimport {\n\ttype MemoryProvider as ContextMemoryProvider,\n\tDEFAULT_LOCAL_MEMORY_EGRESS_POLICY,\n} from \"./context/memory-provider-contract.ts\";\nimport { type MemoryRetrievalReport, retrieveMemoryForContext } from \"./context/memory-retrieval.ts\";\nimport { createOkfMemoryProvider } from \"./context/okf-memory-provider.ts\";\nimport { EffectivenessTracker } from \"./memory/effectiveness-tracker.ts\";\nimport { MemoryManager } from \"./memory/memory-manager.ts\";\nimport type { MemoryProvider } from \"./memory/memory-provider.ts\";\nimport { FileStoreProvider } from \"./memory/providers/file-store.ts\";\nimport { TranscriptRecallProvider } from \"./memory/providers/transcript-recall.ts\";\nimport { wrapUntrustedText } from \"./security/untrusted-boundary.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\n/**\n * Text of the most recent user message, or \"\" if there is none (e.g. goal-continuation\n * turns with no new user input). An empty query degrades to zero memory-retrieval results\n * by construction (see memory-provider-contract.ts's score-on-empty-query-tokens rule) --\n * no special-casing needed here beyond returning \"\".\n */\nfunction latestUserMessageText(messages: AgentMessage[]): string {\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tconst message = messages[index];\n\t\tif (message.role !== \"user\") continue;\n\t\tif (typeof message.content === \"string\") return message.content;\n\t\tconst parts: string[] = [];\n\t\tfor (const part of message.content) {\n\t\t\tif (part.type === \"text\") parts.push(part.text);\n\t\t}\n\t\treturn parts.join(\"\\n\");\n\t}\n\treturn \"\";\n}\n\nfunction emptyMemoryRetrievalReport(maxResults: number): MemoryRetrievalReport {\n\treturn { request: { query: \"\", maxResults }, providerReports: [], results: [], contextItems: [] };\n}\n\nexport interface MemoryControllerDeps {\n\t/** Memory-retrieval + prompt-inclusion settings (the opt-in gates for retrieval and surfacing). */\n\tgetSettingsManager(): SettingsManager;\n\t/** Current turn index, stamped into a retrieval request's `createdAtTurn`. */\n\tgetTurnIndex(): number;\n\t/** Agent root — the durable OKF memory docs live under `<agentDir>/okf-memory`. */\n\tgetAgentDir(): string;\n\t/** Workspace root, passed to provider initialization. */\n\tgetCwd(): string;\n\t/** This session's id, passed to provider initialization. */\n\tgetSessionId(): string;\n\t/** Child sessions gate durable memory writes; passed to provider initialization. */\n\tisChildSession(): boolean;\n\t/** Re-derive the tool registry after (re)init so the newly-surfaced memory tools take effect. */\n\trefreshToolRegistry(): void;\n}\n\nexport class MemoryController {\n\tprivate _memoryOkfProvider: ContextMemoryProvider | undefined = undefined;\n\tprivate _latestMemoryRetrievalReport: MemoryRetrievalReport | undefined = undefined;\n\tprivate _latestMemoryPromptInclusionReport: MemoryPromptInclusionReport | undefined = undefined;\n\t/** Plug-and-play memory subsystem. Recreated on each (re)initialize so reload is safe. */\n\tprivate _memoryManager: MemoryManager = new MemoryManager();\n\t/** R4: tracks whether injected recall is actually used, to adapt the recall gate. */\n\tprivate readonly _effectivenessTracker = new EffectivenessTracker();\n\t/** Memory providers registered by extensions via pi.registerMemoryProvider, applied on (re)init. */\n\tprivate _pendingMemoryProviders: MemoryProvider[] = [];\n\n\tprivate readonly deps: MemoryControllerDeps;\n\n\tconstructor(deps: MemoryControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\t/** The live memory manager. Callers reach prefetch / tool-definitions / markers / shutdown through it. */\n\tgetMemoryManager(): MemoryManager {\n\t\treturn this._memoryManager;\n\t}\n\n\t/**\n\t * Fixed path for this slice's local Pi OKF memory documents, shared across sessions\n\t * under this agentDir (not session-scoped, unlike tool-artifacts/context-gc, since OKF\n\t * memory represents durable cross-session knowledge, not a per-session capture). Not\n\t * yet user-configurable -- see the memory-retrieval settings doc comment.\n\t */\n\tprivate _memoryOkfDir(): string {\n\t\treturn join(this.deps.getAgentDir(), \"okf-memory\");\n\t}\n\n\t/**\n\t * Session-scoped, read-only local OKF memory provider. Lazily created ONLY when memory\n\t * retrieval is enabled (see `runMemoryRetrieval`) -- never force-created, so a session\n\t * with the setting off never touches `_memoryOkfDir()` at all (no directory access, no\n\t * creation; `createOkfMemoryProvider` itself never writes/mkdirs either way).\n\t */\n\tprivate _getMemoryOkfProvider(): ContextMemoryProvider {\n\t\tthis._memoryOkfProvider ??= createOkfMemoryProvider({ rootDir: this._memoryOkfDir() });\n\t\treturn this._memoryOkfProvider;\n\t}\n\n\t/**\n\t * Observe-only local memory retrieval (see context/memory-retrieval.ts and\n\t * context/okf-memory-provider.ts): default disabled, opt-in setting. When disabled,\n\t * never constructs the OKF provider (no directory access under `_memoryOkfDir()` at\n\t * all) and returns an empty report -- fully fail-closed. When enabled, queries the\n\t * local, read-only OKF provider with the latest user message text (empty if there is\n\t * none, e.g. a goal-continuation turn -- degrades to zero results by construction, see\n\t * `latestUserMessageText`'s doc comment) under `DEFAULT_LOCAL_MEMORY_EGRESS_POLICY`.\n\t * Retrieved items are only ever stored in the report; nothing here touches `messages`,\n\t * the transcript, or the provider-visible prompt. Never throws into a live turn: any\n\t * failure (including a provider search error) degrades to an empty report.\n\t */\n\tasync runMemoryRetrieval(messages: AgentMessage[]): Promise<MemoryRetrievalReport> {\n\t\ttry {\n\t\t\tconst settings = this.deps.getSettingsManager().getMemoryRetrievalSettings();\n\t\t\tif (!settings.enabled) {\n\t\t\t\tconst report = emptyMemoryRetrievalReport(settings.maxResults);\n\t\t\t\tthis._latestMemoryRetrievalReport = report;\n\t\t\t\treturn report;\n\t\t\t}\n\t\t\tconst report = await retrieveMemoryForContext(\n\t\t\t\t[this._getMemoryOkfProvider()],\n\t\t\t\t{ query: latestUserMessageText(messages), maxResults: settings.maxResults },\n\t\t\t\t{\n\t\t\t\t\tcreatedAtTurn: this.deps.getTurnIndex(),\n\t\t\t\t\tmaxResults: settings.maxResults,\n\t\t\t\t\tdefaultLocalPolicy: DEFAULT_LOCAL_MEMORY_EGRESS_POLICY,\n\t\t\t\t},\n\t\t\t);\n\t\t\tthis._latestMemoryRetrievalReport = report;\n\t\t\treturn report;\n\t\t} catch {\n\t\t\tconst report = emptyMemoryRetrievalReport(0);\n\t\t\tthis._latestMemoryRetrievalReport = report;\n\t\t\treturn report;\n\t\t}\n\t}\n\n\t/** Read-only inspection of the latest memory-retrieval report, for tests/debugging. */\n\tgetMemoryRetrievalReport(): MemoryRetrievalReport {\n\t\treturn this._latestMemoryRetrievalReport ?? emptyMemoryRetrievalReport(0);\n\t}\n\n\t/**\n\t * Bounded prompt-surfacing pilot for local memory evidence (see\n\t * context/memory-prompt-block.ts): opt-in, default disabled, and gated on TWO settings\n\t * (`enabled` AND `includeInPrompt`) plus a non-empty `report.contextItems` -- the first\n\t * two are belt-and-suspenders on top of the fact that `runMemoryRetrieval` already\n\t * leaves `contextItems` empty whenever `enabled` is false, regardless of\n\t * `includeInPrompt`. Reuses the `report` this pass's `runMemoryRetrieval` call already\n\t * computed -- never re-queries the provider here.\n\t *\n\t * Appends exactly one ephemeral `custom`/\"memory_evidence\" message wrapped by\n\t * `wrapUntrustedText` (the same nonce-fenced boundary + always-on system-prompt rule\n\t * used for other untrusted content) to the END of `messages`. This is purely additive\n\t * (never mutates an existing message) and purely transient: `messages` here is the\n\t * array about to be sent to the provider, not `this.agent.state.messages` or anything\n\t * persisted via `sessionManager` -- so the injected message can never reach the\n\t * transcript, regardless of how many times this pass runs.\n\t *\n\t * Also records a `MemoryPromptInclusionReport` (context/memory-diagnostics.ts) at each\n\t * branch below, for context_audit's diagnostic surface only -- this is pure bookkeeping\n\t * alongside the existing branches, not a new branch/condition: the messages returned\n\t * are unchanged by this recording.\n\t */\n\tmaybeAppendMemoryEvidenceBlock(messages: AgentMessage[], report: MemoryRetrievalReport): AgentMessage[] {\n\t\ttry {\n\t\t\tconst settings = this.deps.getSettingsManager().getMemoryRetrievalSettings();\n\t\t\tconst base = {\n\t\t\t\tenabled: settings.enabled,\n\t\t\t\tincludeInPrompt: settings.includeInPrompt,\n\t\t\t\tselectedItemCount: report.contextItems.length,\n\t\t\t};\n\t\t\tif (!settings.enabled) {\n\t\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\t\t...base,\n\t\t\t\t\tstatus: \"disabled\",\n\t\t\t\t\tincludedCount: 0,\n\t\t\t\t\tomittedCount: 0,\n\t\t\t\t\tblockChars: 0,\n\t\t\t\t};\n\t\t\t\treturn messages;\n\t\t\t}\n\t\t\tif (!settings.includeInPrompt) {\n\t\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\t\t...base,\n\t\t\t\t\tstatus: \"include_disabled\",\n\t\t\t\t\tincludedCount: 0,\n\t\t\t\t\tomittedCount: 0,\n\t\t\t\t\tblockChars: 0,\n\t\t\t\t};\n\t\t\t\treturn messages;\n\t\t\t}\n\t\t\tif (report.contextItems.length === 0) {\n\t\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\t\t...base,\n\t\t\t\t\tstatus: \"no_results\",\n\t\t\t\t\tincludedCount: 0,\n\t\t\t\t\tomittedCount: 0,\n\t\t\t\t\tblockChars: 0,\n\t\t\t\t};\n\t\t\t\treturn messages;\n\t\t\t}\n\n\t\t\tconst block = buildMemoryPromptBlock(report.contextItems);\n\t\t\tif (!block.text) {\n\t\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\t\t...base,\n\t\t\t\t\tstatus: \"empty_block\",\n\t\t\t\t\tincludedCount: block.includedCount,\n\t\t\t\t\tomittedCount: block.omittedCount,\n\t\t\t\t\tblockChars: 0,\n\t\t\t\t};\n\t\t\t\treturn messages;\n\t\t\t}\n\n\t\t\tconst wrapped = wrapUntrustedText(block.text, \"memory:pi-okf\");\n\t\t\tconst evidenceMessage: CustomMessage = {\n\t\t\t\trole: \"custom\",\n\t\t\t\tcustomType: \"memory_evidence\",\n\t\t\t\tcontent: [{ type: \"text\", text: wrapped }],\n\t\t\t\tdisplay: false,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t};\n\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\t...base,\n\t\t\t\tstatus: \"included\",\n\t\t\t\tincludedCount: block.includedCount,\n\t\t\t\tomittedCount: block.omittedCount,\n\t\t\t\tblockChars: wrapped.length,\n\t\t\t\tsourceLabel: \"memory:pi-okf\",\n\t\t\t};\n\t\t\treturn [...messages, evidenceMessage];\n\t\t} catch {\n\t\t\t// `base` may not exist yet if the throw happened before it was computed (e.g.\n\t\t\t// settings access or `report.contextItems` itself threw), so this branch cannot\n\t\t\t// rely on it -- fall back to safe, fixed defaults rather than risk referencing\n\t\t\t// a partially-evaluated value.\n\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\tenabled: false,\n\t\t\t\tincludeInPrompt: false,\n\t\t\t\tselectedItemCount: 0,\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tincludedCount: 0,\n\t\t\t\tomittedCount: 0,\n\t\t\t\tblockChars: 0,\n\t\t\t};\n\t\t\treturn messages;\n\t\t}\n\t}\n\n\t/** Read-only inspection of the latest memory-prompt-inclusion decision, for tests/debugging and context_audit. */\n\tgetMemoryPromptInclusionReport(): MemoryPromptInclusionReport {\n\t\treturn this._latestMemoryPromptInclusionReport ?? defaultMemoryPromptInclusionReport();\n\t}\n\n\t/**\n\t * Combines the already-stored, no-arg latest reports (never re-queries the provider or\n\t * touches the OKF directory) into the safe, allow-list-projected shape context_audit\n\t * exposes. See context/memory-diagnostics.ts for why this projection is allow-list\n\t * based rather than a spread-then-delete of the raw report.\n\t */\n\tgetMemoryAuditDiagnostics(): {\n\t\tretrieval: MemoryRetrievalDiagnostics;\n\t\tpromptInclusion: MemoryPromptInclusionReport;\n\t} {\n\t\tconst settings = this.deps.getSettingsManager().getMemoryRetrievalSettings();\n\t\treturn {\n\t\t\tretrieval: sanitizeMemoryRetrievalReportForDiagnostics(this.getMemoryRetrievalReport(), settings),\n\t\t\tpromptInclusion: this.getMemoryPromptInclusionReport(),\n\t\t};\n\t}\n\n\t/**\n\t * Zero-I/O gate for cross-session recall (R3): skip trivial turns (short acks, slash commands) so\n\t * recall only runs when it could plausibly help. The provider's similarity cutoff is the real\n\t * filter — this just avoids the index query on turns that obviously don't warrant it.\n\t */\n\tshouldAttemptRecall(text: string): boolean {\n\t\tconst t = text.trim();\n\t\tif (t.length < 12 || t.startsWith(\"/\")) return false;\n\t\tconst words = t.split(/\\s+/).filter((w) => w.length >= 3);\n\t\t// R4 adaptive gate: if recall has rarely been used lately (enough samples to trust the signal),\n\t\t// raise the bar so we only recall on clearly substantial turns — and relax it again once recall\n\t\t// starts paying off. Never fully disabled, so the loop can recover.\n\t\tconst recallRarelyUseful =\n\t\t\tthis._effectivenessTracker.sampleCount >= 5 && this._effectivenessTracker.usefulLately() < 0.15;\n\t\treturn words.length >= (recallRarelyUseful ? 6 : 3);\n\t}\n\n\t/** R4: score whether the agent actually used an injected recall page, so the recall gate can adapt. */\n\trecordRecallOutcome(recallText: string, queryText: string, responseText: string): void {\n\t\tthis._effectivenessTracker.recordRecallOutcome(recallText, queryText, responseText);\n\t}\n\n\t/**\n\t * (Re)build the memory subsystem: a fresh MemoryManager (reload-safe), register the bundled\n\t * file-store + any extension-contributed providers, initialize, then surface the memory tools and\n\t * the frozen system-prompt block. Best-effort: never throws into the session lifecycle.\n\t */\n\tasync initialize(): Promise<void> {\n\t\ttry {\n\t\t\t// Release the previous generation's providers (locks/handles) before recreating, so a\n\t\t\t// reload does not orphan the old MemoryManager. No-op on first init / for file-store.\n\t\t\tawait this._memoryManager.shutdownAll().catch(() => {});\n\t\t\tconst manager = new MemoryManager();\n\t\t\tmanager.registerProvider(new FileStoreProvider());\n\t\t\t// Bundled read-only cross-session recall (R3): indexes past-session transcripts and answers\n\t\t\t// prefetch() with a <memory_context> page. Never writes.\n\t\t\tmanager.registerProvider(new TranscriptRecallProvider());\n\t\t\tfor (const provider of this._pendingMemoryProviders) {\n\t\t\t\ttry {\n\t\t\t\t\tmanager.registerProvider(provider);\n\t\t\t\t} catch {\n\t\t\t\t\t// Duplicate name or reserved-tool collision — skip this provider, keep the rest.\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis._memoryManager = manager;\n\t\t\tawait manager.initializeAll(this.deps.getSessionId(), {\n\t\t\t\tagentDir: this.deps.getAgentDir(),\n\t\t\t\tcwd: this.deps.getCwd(),\n\t\t\t\tisChildSession: this.deps.isChildSession(),\n\t\t\t});\n\t\t\t// Surface memory tools + the frozen memory block now that providers are initialized.\n\t\t\t// refreshToolRegistry() ends in setActiveToolsByName(), which rebuilds AND assigns the\n\t\t\t// system prompt (including the memory block), so no explicit _rebuildSystemPrompt is needed.\n\t\t\tthis.deps.refreshToolRegistry();\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Memory subsystem init failed:\", error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n\n\t/** Register a memory provider contributed by an extension; applied on the next memory (re)init. */\n\tregisterMemoryProvider(provider: MemoryProvider): void {\n\t\tif (!this._pendingMemoryProviders.some((p) => p.name === provider.name)) {\n\t\t\tthis._pendingMemoryProviders.push(provider);\n\t\t}\n\t}\n\n\t/** Reload starts memory providers fresh; loaded extensions re-register before the next `initialize()`. */\n\tclearPendingProviders(): void {\n\t\tthis._pendingMemoryProviders = [];\n\t}\n}\n"]}
1
+ {"version":3,"file":"memory-controller.js","sourceRoot":"","sources":["../../src/core/memory-controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Memory controller: the session's plug-and-play memory subsystem — the read-only OKF retrieval\n * provider, the bounded prompt-evidence surfacing pilot, cross-session recall effectiveness, and the\n * live {@link MemoryManager} (bundled file-store + transcript-recall providers plus any extension\n * contributions).\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Owns the lazily-built OKF\n * provider, the latest retrieval/prompt-inclusion reports, the recreated-on-reload MemoryManager, the\n * recall {@link EffectivenessTracker}, and the extension-contributed pending providers. Everything\n * else it needs — settings, the current turn index, agent/workspace dirs, the session id, the\n * child-session flag, and the tool-registry refresh — is reached through narrow deps accessors rather\n * than the whole AgentSession.\n *\n * Context-transform boundary (deliberate): {@link runMemoryRetrieval} and\n * {@link maybeAppendMemoryEvidenceBlock} are invoked from the session's context transform as one-line\n * delegations. This controller deliberately imports no compaction/context-pipeline internals — it only\n * ever reads settings and builds the retrieval report + the bounded evidence block, so the transform\n * stays the single owner of the pass ordering.\n */\n\nimport { join } from \"node:path\";\nimport type { AgentMessage, CustomMessage } from \"@caupulican/pi-agent-core\";\nimport { collectCurrentWorkMemory } from \"./context/current-work-memory.ts\";\nimport { createFileStoreMemoryProvider } from \"./context/file-store-memory-provider.ts\";\nimport { shouldQueryLongTermMemory } from \"./context/long-term-memory-trigger.ts\";\nimport {\n\tdefaultMemoryPromptInclusionReport,\n\ttype MemoryPromptInclusionReport,\n\ttype MemoryRetrievalDiagnostics,\n\tsanitizeMemoryRetrievalReportForDiagnostics,\n} from \"./context/memory-diagnostics.ts\";\nimport { type MemoryPromptBudget, resolveMemoryPromptBudget } from \"./context/memory-prompt-budget.ts\";\nimport {\n\ttype MemoryProvider as ContextMemoryProvider,\n\tDEFAULT_EXTERNAL_MEMORY_EGRESS_POLICY,\n\tDEFAULT_LOCAL_MEMORY_EGRESS_POLICY,\n} from \"./context/memory-provider-contract.ts\";\nimport { type MemoryRetrievalReport, retrieveMemoryForContext } from \"./context/memory-retrieval.ts\";\nimport { composeTieredMemoryPromptBlock, type MemoryTierCandidate } from \"./context/memory-tier-composer.ts\";\nimport { createOkfMemoryProvider } from \"./context/okf-memory-provider.ts\";\nimport type { GoalState } from \"./goals/goal-state.ts\";\nimport { EffectivenessTracker } from \"./memory/effectiveness-tracker.ts\";\nimport { MemoryManager } from \"./memory/memory-manager.ts\";\nimport type { MemoryProvider } from \"./memory/memory-provider.ts\";\nimport { FileStoreProvider } from \"./memory/providers/file-store.ts\";\nimport { TranscriptRecallProvider } from \"./memory/providers/transcript-recall.ts\";\nimport { wrapUntrustedText } from \"./security/untrusted-boundary.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\n/**\n * Text of the most recent user message, or \"\" if there is none (e.g. goal-continuation\n * turns with no new user input). An empty query is still valid: standing user preferences may\n * surface from the file-store retrieval fallback when the static memory prompt cannot fit,\n * while long-term providers remain gated by shouldQueryLongTermMemory().\n */\nfunction latestUserMessageText(messages: AgentMessage[]): string {\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tconst message = messages[index];\n\t\tif (message.role !== \"user\") continue;\n\t\tif (typeof message.content === \"string\") return message.content;\n\t\tconst parts: string[] = [];\n\t\tfor (const part of message.content) {\n\t\t\tif (part.type === \"text\") parts.push(part.text);\n\t\t}\n\t\treturn parts.join(\"\\n\");\n\t}\n\treturn \"\";\n}\n\nfunction emptyMemoryRetrievalReport(maxResults: number): MemoryRetrievalReport {\n\treturn { request: { query: \"\", maxResults }, providerReports: [], results: [], contextItems: [] };\n}\n\nconst ENABLED_EXTERNAL_MEMORY_EGRESS_POLICY = {\n\t...DEFAULT_EXTERNAL_MEMORY_EGRESS_POLICY,\n\tenabled: true,\n\tallowedScopes: DEFAULT_LOCAL_MEMORY_EGRESS_POLICY.allowedScopes,\n\tallowExternalEgress: true,\n} as const;\n\nexport interface MemoryControllerDeps {\n\t/** Memory-retrieval + prompt-inclusion settings (default-on gates for retrieval and surfacing). */\n\tgetSettingsManager(): SettingsManager;\n\t/** Current turn index, stamped into a retrieval request's `createdAtTurn`. */\n\tgetTurnIndex(): number;\n\t/** Agent root — the durable OKF memory docs live under `<agentDir>/okf-memory`. */\n\tgetAgentDir(): string;\n\t/** Workspace root, passed to provider initialization. */\n\tgetCwd(): string;\n\t/** This session's id, passed to provider initialization. */\n\tgetSessionId(): string;\n\t/** Child sessions gate durable memory writes; passed to provider initialization. */\n\tisChildSession(): boolean;\n\t/** Re-derive the tool registry after (re)init so the newly-surfaced memory tools take effect. */\n\trefreshToolRegistry(): void;\n\t/** Active model context window, used to cap prompt-visible memory. */\n\tgetContextWindow(): number | undefined;\n\t/** Latest active goal state, used for short-term current-work memory. */\n\tgetGoalState(): GoalState | undefined;\n}\n\n/** Extension-contributed memory state staged across an atomic runtime reload. */\nexport interface MemoryControllerReloadSnapshot {\n\tpendingMemoryProviders: MemoryProvider[];\n\tpendingContextMemoryProviders: ContextMemoryProvider[];\n\tmemoryOkfProvider: ContextMemoryProvider | undefined;\n\tfileStoreMemoryProvider: ContextMemoryProvider | undefined;\n}\n\nexport class MemoryController {\n\tprivate _memoryOkfProvider: ContextMemoryProvider | undefined = undefined;\n\tprivate _fileStoreMemoryProvider: ContextMemoryProvider | undefined = undefined;\n\tprivate _latestMemoryRetrievalReport: MemoryRetrievalReport | undefined = undefined;\n\tprivate _latestMemoryPromptInclusionReport: MemoryPromptInclusionReport | undefined = undefined;\n\t/** Plug-and-play memory subsystem. Recreated on each (re)initialize so reload is safe. */\n\tprivate _memoryManager: MemoryManager = new MemoryManager();\n\t/** R4: tracks whether injected recall is actually used, to adapt the recall gate. */\n\tprivate readonly _effectivenessTracker = new EffectivenessTracker();\n\t/** Memory providers registered by extensions via pi.registerMemoryProvider, applied on (re)init. */\n\tprivate _pendingMemoryProviders: MemoryProvider[] = [];\n\t/** Context-memory providers registered by extensions via pi.registerContextMemoryProvider. */\n\tprivate _pendingContextMemoryProviders: ContextMemoryProvider[] = [];\n\n\tprivate readonly deps: MemoryControllerDeps;\n\n\tconstructor(deps: MemoryControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\t/** The live memory manager. Callers reach prefetch / tool-definitions / markers / shutdown through it. */\n\tgetMemoryManager(): MemoryManager {\n\t\treturn this._memoryManager;\n\t}\n\n\t/**\n\t * Fixed path for this slice's local Pi OKF memory documents, shared across sessions\n\t * under this agentDir (not session-scoped, unlike tool-artifacts/context-gc, since OKF\n\t * memory represents durable cross-session knowledge, not a per-session capture). Not\n\t * yet user-configurable -- see the memory-retrieval settings doc comment.\n\t */\n\tprivate _memoryOkfDir(): string {\n\t\treturn join(this.deps.getAgentDir(), \"okf-memory\");\n\t}\n\n\t/**\n\t * Session-scoped, read-only local OKF memory provider. Lazily created ONLY when memory\n\t * retrieval is enabled (see `runMemoryRetrieval`) -- never force-created, so a session\n\t * with the setting off never touches `_memoryOkfDir()` at all (no directory access, no\n\t * creation; `createOkfMemoryProvider` itself never writes/mkdirs either way).\n\t */\n\tprivate _getMemoryOkfProvider(): ContextMemoryProvider {\n\t\tthis._memoryOkfProvider ??= createOkfMemoryProvider({ rootDir: this._memoryOkfDir() });\n\t\treturn this._memoryOkfProvider;\n\t}\n\n\tprivate _getFileStoreMemoryProvider(): ContextMemoryProvider {\n\t\tthis._fileStoreMemoryProvider ??= createFileStoreMemoryProvider({\n\t\t\tmemoryFilePath: join(this.deps.getAgentDir(), \"MEMORY.md\"),\n\t\t\tuserFilePath: join(this.deps.getAgentDir(), \"USER.md\"),\n\t\t});\n\t\treturn this._fileStoreMemoryProvider;\n\t}\n\n\tprivate _memoryBudget(configuredMaxResults: number) {\n\t\treturn resolveMemoryPromptBudget({\n\t\t\tcontextWindow: this.deps.getContextWindow(),\n\t\t\tconfiguredMaxResults,\n\t\t});\n\t}\n\n\tprivate _shouldQueryFileStoreFallback(budget: MemoryPromptBudget): boolean {\n\t\t// MEMORY.md/USER.md are already injected through the static file-store prompt on\n\t\t// normal windows, so querying them again would duplicate provider-visible memory.\n\t\t// In compact windows the static block can be omitted because it does not fit; only\n\t\t// then use the retrieval view to surface a few budget-pruned lines.\n\t\tif (!budget.enabled || !budget.compact) return false;\n\t\treturn this._memoryManager.buildSystemPromptBlock(budget).trim().length === 0;\n\t}\n\n\t/**\n\t * Observe-only local memory retrieval (see context/memory-retrieval.ts and\n\t * context/okf-memory-provider.ts): default-on, but settings-gated. When disabled,\n\t * never constructs built-in context-memory providers (no directory access under\n\t * `_memoryOkfDir()` at all) and returns an empty report -- fully fail-closed. When enabled,\n\t * queries local, read-only providers with the latest user message text (empty if there is\n\t * none, e.g. a goal-continuation turn) under `DEFAULT_LOCAL_MEMORY_EGRESS_POLICY`.\n\t * Retrieved items are only ever stored in the report; nothing here touches `messages`,\n\t * the transcript, or the provider-visible prompt. Never throws into a live turn: any\n\t * failure (including a provider search error) degrades to an empty report.\n\t */\n\tasync runMemoryRetrieval(messages: AgentMessage[]): Promise<MemoryRetrievalReport> {\n\t\ttry {\n\t\t\tconst settings = this.deps.getSettingsManager().getMemoryRetrievalSettings();\n\t\t\tif (!settings.enabled) {\n\t\t\t\tconst report = emptyMemoryRetrievalReport(settings.maxResults);\n\t\t\t\tthis._latestMemoryRetrievalReport = report;\n\t\t\t\treturn report;\n\t\t\t}\n\t\t\tconst query = latestUserMessageText(messages);\n\t\t\tconst budget = this._memoryBudget(settings.maxResults);\n\t\t\tconst currentWork = collectCurrentWorkMemory({ goalState: this.deps.getGoalState() });\n\t\t\tconst longTermDecision = shouldQueryLongTermMemory({\n\t\t\t\tlatestUserText: query,\n\t\t\t\tgoalState: this.deps.getGoalState(),\n\t\t\t\tbudget,\n\t\t\t\tcurrentWorkCandidateCount: currentWork.length,\n\t\t\t});\n\t\t\tconst providers = this._shouldQueryFileStoreFallback(budget) ? [this._getFileStoreMemoryProvider()] : [];\n\t\t\tif (longTermDecision.shouldQuery) {\n\t\t\t\tproviders.push(this._getMemoryOkfProvider(), ...this._pendingContextMemoryProviders);\n\t\t\t}\n\t\t\tconst maxResults = budget.enabled ? Math.min(settings.maxResults, budget.maxResults) : settings.maxResults;\n\t\t\tconst report = await retrieveMemoryForContext(\n\t\t\t\tproviders,\n\t\t\t\t{ query, maxResults },\n\t\t\t\t{\n\t\t\t\t\tcreatedAtTurn: this.deps.getTurnIndex(),\n\t\t\t\t\tmaxResults,\n\t\t\t\t\tdefaultLocalPolicy: DEFAULT_LOCAL_MEMORY_EGRESS_POLICY,\n\t\t\t\t\tdefaultExternalPolicy: settings.allowExternalEgress\n\t\t\t\t\t\t? ENABLED_EXTERNAL_MEMORY_EGRESS_POLICY\n\t\t\t\t\t\t: DEFAULT_EXTERNAL_MEMORY_EGRESS_POLICY,\n\t\t\t\t},\n\t\t\t);\n\t\t\tthis._latestMemoryRetrievalReport = report;\n\t\t\treturn report;\n\t\t} catch {\n\t\t\tconst report = emptyMemoryRetrievalReport(0);\n\t\t\tthis._latestMemoryRetrievalReport = report;\n\t\t\treturn report;\n\t\t}\n\t}\n\n\t/** Read-only inspection of the latest memory-retrieval report, for tests/debugging. */\n\tgetMemoryRetrievalReport(): MemoryRetrievalReport {\n\t\treturn this._latestMemoryRetrievalReport ?? emptyMemoryRetrievalReport(0);\n\t}\n\n\tprivate _candidateForContextItem(\n\t\titem: MemoryRetrievalReport[\"contextItems\"][number],\n\t): MemoryTierCandidate | undefined {\n\t\tconst summary = item.summary?.trim();\n\t\tif (!summary) return undefined;\n\t\tconst ref = item.primaryRef?.type === \"memory\" ? item.primaryRef.ref : undefined;\n\t\tconst sourceLabel =\n\t\t\tref?.providerId === \"pi-file-store\" && ref.kind === \"user_preference\"\n\t\t\t\t? \"rule:user\"\n\t\t\t\t: `memory:${ref?.providerId ?? item.source}`;\n\t\tconst tier = ref?.kind === \"user_preference\" ? \"standing\" : \"long_term\";\n\t\treturn {\n\t\t\tid: item.id,\n\t\t\ttier,\n\t\t\tsourceLabel,\n\t\t\tsummary,\n\t\t\tscore: 0.5,\n\t\t\tevidenceRefs: item.evidenceRefs,\n\t\t};\n\t}\n\n\tprivate _memoryCandidates(report: MemoryRetrievalReport): MemoryTierCandidate[] {\n\t\treturn [\n\t\t\t...collectCurrentWorkMemory({ goalState: this.deps.getGoalState() }),\n\t\t\t...report.contextItems.flatMap((item) => {\n\t\t\t\tconst candidate = this._candidateForContextItem(item);\n\t\t\t\treturn candidate === undefined ? [] : [candidate];\n\t\t\t}),\n\t\t];\n\t}\n\n\t/**\n\t * Bounded prompt-surfacing for local memory evidence (see context/memory-tier-composer.ts):\n\t * default-on, but gated on TWO settings (`enabled` AND `includeInPrompt`) plus at least one\n\t * current-work, standing, or retrieved memory candidate -- the first\n\t * two are belt-and-suspenders on top of the fact that `runMemoryRetrieval` already\n\t * leaves `contextItems` empty whenever `enabled` is false, regardless of\n\t * `includeInPrompt`. Reuses the `report` this pass's `runMemoryRetrieval` call already\n\t * computed -- never re-queries the provider here.\n\t *\n\t * Appends exactly one ephemeral `custom`/\"memory_evidence\" message wrapped by\n\t * `wrapUntrustedText` (the same nonce-fenced boundary + always-on system-prompt rule\n\t * used for other untrusted content) to the END of `messages`. This is purely additive\n\t * (never mutates an existing message) and purely transient: `messages` here is the\n\t * array about to be sent to the provider, not `this.agent.state.messages` or anything\n\t * persisted via `sessionManager` -- so the injected message can never reach the\n\t * transcript, regardless of how many times this pass runs.\n\t *\n\t * Also records a `MemoryPromptInclusionReport` (context/memory-diagnostics.ts) at each\n\t * branch below, for context_audit's diagnostic surface only -- this is pure bookkeeping\n\t * alongside the existing branches, not a new branch/condition: the messages returned\n\t * are unchanged by this recording.\n\t */\n\tmaybeAppendMemoryEvidenceBlock(messages: AgentMessage[], report: MemoryRetrievalReport): AgentMessage[] {\n\t\ttry {\n\t\t\tconst settings = this.deps.getSettingsManager().getMemoryRetrievalSettings();\n\t\t\tconst candidates = this._memoryCandidates(report);\n\t\t\tconst base = {\n\t\t\t\tenabled: settings.enabled,\n\t\t\t\tincludeInPrompt: settings.includeInPrompt,\n\t\t\t\tselectedItemCount: candidates.length,\n\t\t\t};\n\t\t\tif (!settings.enabled) {\n\t\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\t\t...base,\n\t\t\t\t\tstatus: \"disabled\",\n\t\t\t\t\tincludedCount: 0,\n\t\t\t\t\tomittedCount: 0,\n\t\t\t\t\tblockChars: 0,\n\t\t\t\t};\n\t\t\t\treturn messages;\n\t\t\t}\n\t\t\tif (!settings.includeInPrompt) {\n\t\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\t\t...base,\n\t\t\t\t\tstatus: \"include_disabled\",\n\t\t\t\t\tincludedCount: 0,\n\t\t\t\t\tomittedCount: 0,\n\t\t\t\t\tblockChars: 0,\n\t\t\t\t};\n\t\t\t\treturn messages;\n\t\t\t}\n\t\t\tif (candidates.length === 0) {\n\t\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\t\t...base,\n\t\t\t\t\tstatus: \"no_results\",\n\t\t\t\t\tincludedCount: 0,\n\t\t\t\t\tomittedCount: 0,\n\t\t\t\t\tblockChars: 0,\n\t\t\t\t};\n\t\t\t\treturn messages;\n\t\t\t}\n\n\t\t\tconst budget = this._memoryBudget(settings.maxResults);\n\t\t\tconst block = composeTieredMemoryPromptBlock(candidates, budget);\n\t\t\tif (!block.text) {\n\t\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\t\t...base,\n\t\t\t\t\tstatus: \"empty_block\",\n\t\t\t\t\tincludedCount: block.includedCount,\n\t\t\t\t\tomittedCount: block.omittedCount,\n\t\t\t\t\tblockChars: 0,\n\t\t\t\t};\n\t\t\t\treturn messages;\n\t\t\t}\n\n\t\t\tconst wrapped = wrapUntrustedText(block.text, \"memory:tiered\");\n\t\t\tconst evidenceMessage: CustomMessage = {\n\t\t\t\trole: \"custom\",\n\t\t\t\tcustomType: \"memory_evidence\",\n\t\t\t\tcontent: [{ type: \"text\", text: wrapped }],\n\t\t\t\tdisplay: false,\n\t\t\t\ttimestamp: Date.now(),\n\t\t\t};\n\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\t...base,\n\t\t\t\tstatus: \"included\",\n\t\t\t\tincludedCount: block.includedCount,\n\t\t\t\tomittedCount: block.omittedCount,\n\t\t\t\tblockChars: wrapped.length,\n\t\t\t\tsourceLabel: \"memory:tiered\",\n\t\t\t};\n\t\t\treturn [...messages, evidenceMessage];\n\t\t} catch {\n\t\t\t// `base` may not exist yet if the throw happened before it was computed (e.g.\n\t\t\t// settings access or `report.contextItems` itself threw), so this branch cannot\n\t\t\t// rely on it -- fall back to safe, fixed defaults rather than risk referencing\n\t\t\t// a partially-evaluated value.\n\t\t\tthis._latestMemoryPromptInclusionReport = {\n\t\t\t\tenabled: false,\n\t\t\t\tincludeInPrompt: false,\n\t\t\t\tselectedItemCount: 0,\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tincludedCount: 0,\n\t\t\t\tomittedCount: 0,\n\t\t\t\tblockChars: 0,\n\t\t\t};\n\t\t\treturn messages;\n\t\t}\n\t}\n\n\t/** Read-only inspection of the latest memory-prompt-inclusion decision, for tests/debugging and context_audit. */\n\tgetMemoryPromptInclusionReport(): MemoryPromptInclusionReport {\n\t\treturn this._latestMemoryPromptInclusionReport ?? defaultMemoryPromptInclusionReport();\n\t}\n\n\t/**\n\t * Combines the already-stored, no-arg latest reports (never re-queries the provider or\n\t * touches the OKF directory) into the safe, allow-list-projected shape context_audit\n\t * exposes. See context/memory-diagnostics.ts for why this projection is allow-list\n\t * based rather than a spread-then-delete of the raw report.\n\t */\n\tgetMemoryAuditDiagnostics(): {\n\t\tretrieval: MemoryRetrievalDiagnostics;\n\t\tpromptInclusion: MemoryPromptInclusionReport;\n\t} {\n\t\tconst settings = this.deps.getSettingsManager().getMemoryRetrievalSettings();\n\t\treturn {\n\t\t\tretrieval: sanitizeMemoryRetrievalReportForDiagnostics(this.getMemoryRetrievalReport(), settings),\n\t\t\tpromptInclusion: this.getMemoryPromptInclusionReport(),\n\t\t};\n\t}\n\n\t/**\n\t * Zero-I/O gate for cross-session recall (R3): skip trivial turns (short acks, slash commands) so\n\t * recall only runs when it could plausibly help. The provider's similarity cutoff is the real\n\t * filter — this just avoids the index query on turns that obviously don't warrant it.\n\t */\n\tshouldAttemptRecall(text: string): boolean {\n\t\tif (!this.deps.getSettingsManager().getMemoryRetrievalSettings().enabled) return false;\n\t\tconst t = text.trim();\n\t\tif (t.length < 12 || t.startsWith(\"/\")) return false;\n\t\tconst words = t.split(/\\s+/).filter((w) => w.length >= 3);\n\t\t// R4 adaptive gate: if recall has rarely been used lately (enough samples to trust the signal),\n\t\t// raise the bar so we only recall on clearly substantial turns — and relax it again once recall\n\t\t// starts paying off. Never fully disabled, so the loop can recover.\n\t\tconst recallRarelyUseful =\n\t\t\tthis._effectivenessTracker.sampleCount >= 5 && this._effectivenessTracker.usefulLately() < 0.15;\n\t\treturn words.length >= (recallRarelyUseful ? 6 : 3);\n\t}\n\n\t/** Legacy recall prefetch with the same hard-off and explicit external-egress policy as context retrieval. */\n\tasync prefetchRecall(query: string): Promise<string> {\n\t\tconst settings = this.deps.getSettingsManager().getMemoryRetrievalSettings();\n\t\tif (!settings.enabled) return \"\";\n\t\treturn this._memoryManager.prefetch(query, {\n\t\t\texternalEgressPolicy: settings.allowExternalEgress\n\t\t\t\t? ENABLED_EXTERNAL_MEMORY_EGRESS_POLICY\n\t\t\t\t: DEFAULT_EXTERNAL_MEMORY_EGRESS_POLICY,\n\t\t});\n\t}\n\n\t/** R4: score whether the agent actually used an injected recall page, so the recall gate can adapt. */\n\trecordRecallOutcome(recallText: string, queryText: string, responseText: string): void {\n\t\tthis._effectivenessTracker.recordRecallOutcome(recallText, queryText, responseText);\n\t}\n\n\t/**\n\t * (Re)build the memory subsystem: a fresh MemoryManager (reload-safe), register the bundled\n\t * file-store + any extension-contributed providers, initialize, then surface the memory tools and\n\t * the frozen system-prompt block. Best-effort: never throws into the session lifecycle.\n\t */\n\tasync initialize(): Promise<void> {\n\t\ttry {\n\t\t\t// Release the previous generation's providers (locks/handles) before recreating, so a\n\t\t\t// reload does not orphan the old MemoryManager. No-op on first init / for file-store.\n\t\t\tawait this._memoryManager.shutdownAll().catch(() => {});\n\t\t\tconst manager = new MemoryManager();\n\t\t\tmanager.registerProvider(new FileStoreProvider());\n\t\t\t// Bundled read-only cross-session recall (R3): indexes past-session transcripts and answers\n\t\t\t// prefetch() with a <memory_context> page. Never writes.\n\t\t\tmanager.registerProvider(new TranscriptRecallProvider());\n\t\t\tfor (const provider of this._pendingMemoryProviders) {\n\t\t\t\ttry {\n\t\t\t\t\tmanager.registerProvider(provider);\n\t\t\t\t} catch {\n\t\t\t\t\t// Duplicate name or reserved-tool collision — skip this provider, keep the rest.\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis._memoryManager = manager;\n\t\t\tawait manager.initializeAll(this.deps.getSessionId(), {\n\t\t\t\tagentDir: this.deps.getAgentDir(),\n\t\t\t\tcwd: this.deps.getCwd(),\n\t\t\t\tisChildSession: this.deps.isChildSession(),\n\t\t\t});\n\t\t\t// Surface memory tools + the frozen memory block now that providers are initialized.\n\t\t\t// refreshToolRegistry() ends in setActiveToolsByName(), which rebuilds AND assigns the\n\t\t\t// system prompt (including the memory block), so no explicit _rebuildSystemPrompt is needed.\n\t\t\tthis.deps.refreshToolRegistry();\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Memory subsystem init failed:\", error instanceof Error ? error.message : String(error));\n\t\t}\n\t}\n\n\t/** Register a memory provider contributed by an extension; applied on the next memory (re)init. */\n\tregisterMemoryProvider(provider: MemoryProvider): void {\n\t\tif (!this._pendingMemoryProviders.some((p) => p.name === provider.name)) {\n\t\t\tthis._pendingMemoryProviders.push(provider);\n\t\t}\n\t}\n\n\t/** Register a retrieval-style context memory provider contributed by an extension. */\n\tregisterContextMemoryProvider(provider: ContextMemoryProvider): void {\n\t\tif (!this._pendingContextMemoryProviders.some((p) => p.id === provider.id)) {\n\t\t\tthis._pendingContextMemoryProviders.push(provider);\n\t\t}\n\t}\n\n\tcreateReloadSnapshot(): MemoryControllerReloadSnapshot {\n\t\treturn {\n\t\t\tpendingMemoryProviders: [...this._pendingMemoryProviders],\n\t\t\tpendingContextMemoryProviders: [...this._pendingContextMemoryProviders],\n\t\t\tmemoryOkfProvider: this._memoryOkfProvider,\n\t\t\tfileStoreMemoryProvider: this._fileStoreMemoryProvider,\n\t\t};\n\t}\n\n\trestoreReloadSnapshot(snapshot: MemoryControllerReloadSnapshot): void {\n\t\tthis._pendingMemoryProviders = [...snapshot.pendingMemoryProviders];\n\t\tthis._pendingContextMemoryProviders = [...snapshot.pendingContextMemoryProviders];\n\t\tthis._memoryOkfProvider = snapshot.memoryOkfProvider;\n\t\tthis._fileStoreMemoryProvider = snapshot.fileStoreMemoryProvider;\n\t}\n\n\t/** Reload starts memory providers fresh; loaded extensions re-register before the next `initialize()`. */\n\tclearPendingProviders(): void {\n\t\tthis._pendingMemoryProviders = [];\n\t\tthis._pendingContextMemoryProviders = [];\n\t\tthis._memoryOkfProvider = undefined;\n\t\tthis._fileStoreMemoryProvider = undefined;\n\t}\n}\n"]}
@@ -8,6 +8,7 @@ import { clearConfigValueCache } from "./resolve-config-value.ts";
8
8
  declare const ModelOverrideSchema: Type.TObject<{
9
9
  name: Type.TOptional<Type.TString>;
10
10
  reasoning: Type.TOptional<Type.TBoolean>;
11
+ defaultThinkingLevel: Type.TOptional<Type.TUnion<[Type.TLiteral<"minimal">, Type.TLiteral<"low">, Type.TLiteral<"medium">, Type.TLiteral<"high">, Type.TLiteral<"xhigh">, Type.TLiteral<"max">, Type.TLiteral<"ultra">]>>;
11
12
  thinkingLevelMap: Type.TOptional<Type.TObject<{
12
13
  off: Type.TOptional<Type.TUnion<[Type.TString, Type.TNull]>>;
13
14
  minimal: Type.TOptional<Type.TUnion<[Type.TString, Type.TNull]>>;
@@ -15,6 +16,8 @@ declare const ModelOverrideSchema: Type.TObject<{
15
16
  medium: Type.TOptional<Type.TUnion<[Type.TString, Type.TNull]>>;
16
17
  high: Type.TOptional<Type.TUnion<[Type.TString, Type.TNull]>>;
17
18
  xhigh: Type.TOptional<Type.TUnion<[Type.TString, Type.TNull]>>;
19
+ max: Type.TOptional<Type.TUnion<[Type.TString, Type.TNull]>>;
20
+ ultra: Type.TOptional<Type.TUnion<[Type.TString, Type.TNull]>>;
18
21
  }>>;
19
22
  input: Type.TOptional<Type.TArray<Type.TUnion<[Type.TLiteral<"text">, Type.TLiteral<"image">]>>>;
20
23
  cost: Type.TOptional<Type.TObject<{
@@ -92,6 +95,11 @@ declare const ModelOverrideSchema: Type.TObject<{
92
95
  }>]>>;
93
96
  }>;
94
97
  type ModelOverride = Static<typeof ModelOverrideSchema>;
98
+ interface ProviderRequestConfig {
99
+ apiKey?: string;
100
+ headers?: Record<string, string>;
101
+ authHeader?: boolean;
102
+ }
95
103
  export type ResolvedRequestAuth = {
96
104
  ok: true;
97
105
  apiKey?: string;
@@ -157,6 +165,8 @@ export declare class ModelRegistry {
157
165
  * Get API key and request headers for a model.
158
166
  */
159
167
  getApiKeyAndHeaders(model: Model<Api>): Promise<ResolvedRequestAuth>;
168
+ /** Recover a locally stored OAuth credential rejected by the provider. */
169
+ recoverRejectedOAuthApiKey(providerId: string, rejectedApiKey: string): Promise<string | undefined>;
160
170
  /**
161
171
  * Return auth status for a provider, including request auth configured in models.json.
162
172
  * This intentionally does not execute command-backed config values.
@@ -182,6 +192,10 @@ export declare class ModelRegistry {
182
192
  * If provider has oauth: registers OAuth provider for /login support.
183
193
  */
184
194
  registerProvider(providerName: string, config: ProviderConfigInput): void;
195
+ /** Capture all mutable registry state before a runtime generation is rebuilt. */
196
+ createReloadSnapshot(): ModelRegistryReloadSnapshot;
197
+ /** Restore a captured generation, including global API/OAuth registrations. */
198
+ restoreReloadSnapshot(snapshot: ModelRegistryReloadSnapshot): void;
185
199
  /**
186
200
  * Unregister a previously registered provider.
187
201
  *
@@ -192,6 +206,8 @@ export declare class ModelRegistry {
192
206
  * Has no effect if the provider was never registered.
193
207
  */
194
208
  unregisterProvider(providerName: string): void;
209
+ /** Remove a provider generation in one refresh so surviving dynamic streams remain registered. */
210
+ unregisterProviders(providerNames: Iterable<string>): void;
195
211
  /**
196
212
  * Upsert a provider config into registeredProviders.
197
213
  * If the provider is already registered, defined values in the incoming config
@@ -200,6 +216,7 @@ export declare class ModelRegistry {
200
216
  */
201
217
  private upsertRegisteredProvider;
202
218
  private validateProviderConfig;
219
+ private registerProviderIntegrations;
203
220
  private applyProviderConfig;
204
221
  }
205
222
  /**
@@ -221,6 +238,7 @@ export interface ProviderConfigInput {
221
238
  api?: Api;
222
239
  baseUrl?: string;
223
240
  reasoning: boolean;
241
+ defaultThinkingLevel?: Model<Api>["defaultThinkingLevel"];
224
242
  thinkingLevelMap?: Model<Api>["thinkingLevelMap"];
225
243
  input: ("text" | "image")[];
226
244
  cost: {
@@ -237,5 +255,12 @@ export interface ProviderConfigInput {
237
255
  }>;
238
256
  modelOverrides?: Record<string, ModelOverride>;
239
257
  }
258
+ export interface ModelRegistryReloadSnapshot {
259
+ models: Model<Api>[];
260
+ providerRequestConfigs: Map<string, ProviderRequestConfig>;
261
+ modelRequestHeaders: Map<string, Record<string, string>>;
262
+ registeredProviders: Map<string, ProviderConfigInput>;
263
+ loadError: string | undefined;
264
+ }
240
265
  export {};
241
266
  //# sourceMappingURL=model-registry.d.ts.map