@xopcai/xopc 0.0.140 → 0.0.142

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 (250) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/xopc.extension.json +1 -1
  3. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DtONoxBN.js → agent-avatar-dicebear-BawPxiu5.js} +1 -1
  4. package/dist/gateway/static/root/assets/agents-CyvCIHnB.js +2 -0
  5. package/dist/gateway/static/root/assets/{app-management-settings-panel-BRBxLECY.js → app-management-settings-panel-81xd1p9R.js} +1 -1
  6. package/dist/gateway/static/root/assets/{appearance-settings-ChvyuSEt.js → appearance-settings-DYeQsYU8.js} +1 -1
  7. package/dist/gateway/static/root/assets/{apps-page-DgHWTnjs.js → apps-page-DK_Vx6nl.js} +1 -1
  8. package/dist/gateway/static/root/assets/{archive-plugin-CdIoagem.js → archive-plugin-D_oa7oti.js} +1 -1
  9. package/dist/gateway/static/root/assets/{attachment-load-Ch411Kht.js → attachment-load-Cd-bwI2N.js} +1 -1
  10. package/dist/gateway/static/root/assets/{automation-suggestion-card-SVq27yw2.js → automation-suggestion-card-B1LV3YYv.js} +1 -1
  11. package/dist/gateway/static/root/assets/automations-page-fzrovqhv.js +4 -0
  12. package/dist/gateway/static/root/assets/{binary-plugins-D8cV-emn.js → binary-plugins-O8svLKWw.js} +1 -1
  13. package/dist/gateway/static/root/assets/{block-editor-CXDkby6y.js → block-editor-Dhj5f4Bx.js} +47 -47
  14. package/dist/gateway/static/root/assets/browser-settings-page-BDn-n-UB.js +8 -0
  15. package/dist/gateway/static/root/assets/{capability-presets-api-DLxI--Ge.js → capability-presets-api-dJy1Eqgx.js} +1 -1
  16. package/dist/gateway/static/root/assets/{capability-presets-settings-panel-DKyRbnlH.js → capability-presets-settings-panel-Co5ipo_Q.js} +1 -1
  17. package/dist/gateway/static/root/assets/{channel-recipient-api-DllrPmp-.js → channel-recipient-api-Bfuhp2ty.js} +1 -1
  18. package/dist/gateway/static/root/assets/channels-settings-BWlpaxg9.js +1 -0
  19. package/dist/gateway/static/root/assets/channels-status-swr-D_jkIZLV.js +1 -0
  20. package/dist/gateway/static/root/assets/connectors-page-BcekbW4q.js +2 -0
  21. package/dist/gateway/static/root/assets/{desktop-pet-Bz9ommjr.js → desktop-pet-DFMfIb5F.js} +1 -1
  22. package/dist/gateway/static/root/assets/{desktop-pet-settings-BnLgxgoR.js → desktop-pet-settings-BJCUuL8Y.js} +1 -1
  23. package/dist/gateway/static/root/assets/{directory-picker-path-field-DLsCwFaf.js → directory-picker-path-field-BrYt1gAr.js} +1 -1
  24. package/dist/gateway/static/root/assets/{dreaming-settings-BzkF1-XT.js → dreaming-settings-BN1Z2mIo.js} +3 -3
  25. package/dist/gateway/static/root/assets/{extension-debug-page-CdERvf3L.js → extension-debug-page-Bh630pLD.js} +1 -1
  26. package/dist/gateway/static/root/assets/{extension-page-D1fz474Z.js → extension-page-B-1KxvVy.js} +1 -1
  27. package/dist/gateway/static/root/assets/{extension-settings-page-DoQMkNOK.js → extension-settings-page-BF9NACI3.js} +1 -1
  28. package/dist/gateway/static/root/assets/{fetch-BID89uCH.js → fetch-B1WtuprY.js} +1 -1
  29. package/dist/gateway/static/root/assets/{gateway-config-api-Rygo8UAs.js → gateway-config-api-De2N821l.js} +1 -1
  30. package/dist/gateway/static/root/assets/{gateway-settings-kb8KIeQL.js → gateway-settings-DN9sNREz.js} +1 -1
  31. package/dist/gateway/static/root/assets/{gateway-startup-retry-BkrLLlpE.js → gateway-startup-retry-nEujFSDj.js} +1 -1
  32. package/dist/gateway/static/root/assets/goal-create-dialog-BpoQW8cD.js +1 -0
  33. package/dist/gateway/static/root/assets/goal-detail-page-CBRSPd0b.js +4 -0
  34. package/dist/gateway/static/root/assets/{goals-config-api-BuY6-l0a.js → goals-config-api-DUy3eEse.js} +1 -1
  35. package/dist/gateway/static/root/assets/goals-page-BCcBi407.js +2 -0
  36. package/dist/gateway/static/root/assets/{goals-settings-DoH5fZWo.js → goals-settings-pkTXzXkh.js} +1 -1
  37. package/dist/gateway/static/root/assets/{heartbeat-settings-BqDs48B-.js → heartbeat-settings-ezsufgLQ.js} +1 -1
  38. package/dist/gateway/static/root/assets/{index-CrPRlGMg.js → index-DcUaXYeb.js} +65 -65
  39. package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-CVIwwM15.js +1 -0
  40. package/dist/gateway/static/root/assets/logs-page-DQtTRXtj.js +7 -0
  41. package/dist/gateway/static/root/assets/{markdown-split-CSt32FWE.js → markdown-split-B_6us9j7.js} +1 -1
  42. package/dist/gateway/static/root/assets/{media-plugins-ByItHxBj.js → media-plugins-D_fryIyj.js} +1 -1
  43. package/dist/gateway/static/root/assets/messages-BXNELNBa.js +3 -0
  44. package/dist/gateway/static/root/assets/models-hub-panel-BP2M9n6Q.js +2 -0
  45. package/dist/gateway/static/root/assets/notes-page-CspveYaO.js +1 -0
  46. package/dist/gateway/static/root/assets/notes-workbench-BHwzJ0OC.js +2 -0
  47. package/dist/gateway/static/root/assets/{page-tabs-DOfMaq_L.js → page-tabs-D_8jGlqx.js} +1 -1
  48. package/dist/gateway/static/root/assets/{preview-open-alternatives-BArxASu7.js → preview-open-alternatives-BBLMK8Mp.js} +1 -1
  49. package/dist/gateway/static/root/assets/project-detail-page-Dy3rsobs.js +1 -0
  50. package/dist/gateway/static/root/assets/projects-page-CWKpPE0L.js +1 -0
  51. package/dist/gateway/static/root/assets/remote-access-hub-CqNRyXEo.js +1 -0
  52. package/dist/gateway/static/root/assets/{save-bar-controls-BRxlkhG8.js → save-bar-controls-DVS3FR5r.js} +1 -1
  53. package/dist/gateway/static/root/assets/{schedule-field-BzM1iyOc.js → schedule-field-D6vKd6-T.js} +1 -1
  54. package/dist/gateway/static/root/assets/{schema-form-ChVcm0DX.js → schema-form-BPy4lbIh.js} +1 -1
  55. package/dist/gateway/static/root/assets/sessions-page-MEME7ivF.js +1 -0
  56. package/dist/gateway/static/root/assets/settings-advanced-gate-JVd0qaSF.js +1 -0
  57. package/dist/gateway/static/root/assets/{settings-collapsible-section-BcD9xrvY.js → settings-collapsible-section-WzdikxPt.js} +1 -1
  58. package/dist/gateway/static/root/assets/{settings-form-section-Cu30CXVl.js → settings-form-section-CvfzhBV-.js} +1 -1
  59. package/dist/gateway/static/root/assets/{settings-loading-skeleton-Cfd37IYb.js → settings-loading-skeleton-BxR-Ulhi.js} +1 -1
  60. package/dist/gateway/static/root/assets/{settings-page-EINHyzLo.js → settings-page-DYEbB1EV.js} +1 -1
  61. package/dist/gateway/static/root/assets/{settings-page-layout-C15zixct.js → settings-page-layout-CKefvbYi.js} +1 -1
  62. package/dist/gateway/static/root/assets/{setup-status-panel-Bu7SdpD4.js → setup-status-panel-dWN8BJ3z.js} +1 -1
  63. package/dist/gateway/static/root/assets/{share-preview-page-CjjAFV-v.js → share-preview-page-D8foINzP.js} +1 -1
  64. package/dist/gateway/static/root/assets/shares-settings-Tgbv1s7G.js +1 -0
  65. package/dist/gateway/static/root/assets/skill-reload-api-D_YK0b_Z.js +1 -0
  66. package/dist/gateway/static/root/assets/skills-page-DGytfoZK.js +2 -0
  67. package/dist/gateway/static/root/assets/{system-settings-panel-DlQMtzPf.js → system-settings-panel-BokgAgf7.js} +1 -1
  68. package/dist/gateway/static/root/assets/{typed-models-lib-BS1BwExE.js → typed-models-lib-BwHlNL3L.js} +1 -1
  69. package/dist/gateway/static/root/assets/{use-channel-catalog-EA4hfbsP.js → use-channel-catalog-DR11ap3A.js} +1 -1
  70. package/dist/gateway/static/root/assets/{user-profile-settings-panel-B7RCdmSp.js → user-profile-settings-panel-8YtygcJp.js} +1 -1
  71. package/dist/gateway/static/root/assets/{utils-BrhEgVaa.js → utils-BKUWB-TT.js} +1 -1
  72. package/dist/gateway/static/root/assets/voice-api-key-field-BVH71EFf.js +1 -0
  73. package/dist/gateway/static/root/assets/work-item-detail-page-JeTE7XNM.js +1 -0
  74. package/dist/gateway/static/root/assets/workflow-run-setup-panel-CL_rnAUB.js +3 -0
  75. package/dist/gateway/static/root/assets/{workflow-start-dialog-BZCY7N42.js → workflow-start-dialog-_u8Uc1ze.js} +1 -1
  76. package/dist/gateway/static/root/assets/workflows-page-ixgk_y1l.js +18 -0
  77. package/dist/gateway/static/root/index.html +4 -4
  78. package/dist/package.js +1 -1
  79. package/dist/src/agent/agent-manager.js +13 -21
  80. package/dist/src/agent/agent-manager.js.map +1 -1
  81. package/dist/src/agent/background-review/run-background-review.js +1 -0
  82. package/dist/src/agent/background-review/run-background-review.js.map +1 -1
  83. package/dist/src/agent/embedded/session-runner.js +5 -5
  84. package/dist/src/agent/embedded/session-runner.js.map +1 -1
  85. package/dist/src/agent/embedded/xopc-auth-storage.d.ts +4 -3
  86. package/dist/src/agent/embedded/xopc-auth-storage.js +6 -6
  87. package/dist/src/agent/embedded/xopc-auth-storage.js.map +1 -1
  88. package/dist/src/agent/embedded/xopc-stream-bridge.js +1 -0
  89. package/dist/src/agent/embedded/xopc-stream-bridge.js.map +1 -1
  90. package/dist/src/agent/goals/checklist-judge.js +3 -3
  91. package/dist/src/agent/goals/checklist-judge.js.map +1 -1
  92. package/dist/src/agent/goals/judge.js +2 -2
  93. package/dist/src/agent/goals/judge.js.map +1 -1
  94. package/dist/src/agent/image/understanding/pi-ai-provider.js +2 -2
  95. package/dist/src/agent/image/understanding/pi-ai-provider.js.map +1 -1
  96. package/dist/src/agent/memory/access-policy.d.ts +12 -0
  97. package/dist/src/agent/memory/access-policy.js +44 -0
  98. package/dist/src/agent/memory/access-policy.js.map +1 -0
  99. package/dist/src/agent/memory/builtin-provider.d.ts +4 -1
  100. package/dist/src/agent/memory/builtin-provider.js +51 -16
  101. package/dist/src/agent/memory/builtin-provider.js.map +1 -1
  102. package/dist/src/agent/memory/compaction.js +2 -2
  103. package/dist/src/agent/memory/compaction.js.map +1 -1
  104. package/dist/src/agent/memory/context/planner.d.ts +1 -0
  105. package/dist/src/agent/memory/context/planner.js +9 -5
  106. package/dist/src/agent/memory/context/planner.js.map +1 -1
  107. package/dist/src/agent/memory/create-memory-manager.d.ts +1 -1
  108. package/dist/src/agent/memory/create-memory-manager.js +8 -5
  109. package/dist/src/agent/memory/create-memory-manager.js.map +1 -1
  110. package/dist/src/agent/memory/dreaming/config.d.ts +2 -0
  111. package/dist/src/agent/memory/dreaming/config.js +13 -1
  112. package/dist/src/agent/memory/dreaming/config.js.map +1 -1
  113. package/dist/src/agent/memory/dreaming/deep-promotion.d.ts +2 -0
  114. package/dist/src/agent/memory/dreaming/deep-promotion.js +24 -3
  115. package/dist/src/agent/memory/dreaming/deep-promotion.js.map +1 -1
  116. package/dist/src/agent/memory/dreaming/light-sweep.d.ts +1 -0
  117. package/dist/src/agent/memory/dreaming/light-sweep.js +184 -16
  118. package/dist/src/agent/memory/dreaming/light-sweep.js.map +1 -1
  119. package/dist/src/agent/memory/dreaming/promotion-lifecycle.d.ts +30 -0
  120. package/dist/src/agent/memory/dreaming/promotion-lifecycle.js +85 -0
  121. package/dist/src/agent/memory/dreaming/promotion-lifecycle.js.map +1 -0
  122. package/dist/src/agent/memory/dreaming/rem-patterns.d.ts +4 -0
  123. package/dist/src/agent/memory/dreaming/rem-patterns.js +76 -53
  124. package/dist/src/agent/memory/dreaming/rem-patterns.js.map +1 -1
  125. package/dist/src/agent/memory/dreaming/short-term-store.d.ts +10 -0
  126. package/dist/src/agent/memory/dreaming/short-term-store.js +157 -79
  127. package/dist/src/agent/memory/dreaming/short-term-store.js.map +1 -1
  128. package/dist/src/agent/memory/manager.d.ts +7 -0
  129. package/dist/src/agent/memory/manager.js +31 -6
  130. package/dist/src/agent/memory/manager.js.map +1 -1
  131. package/dist/src/agent/memory/memory-config.d.ts +1 -1
  132. package/dist/src/agent/memory/memory-config.js +3 -3
  133. package/dist/src/agent/memory/memory-config.js.map +1 -1
  134. package/dist/src/agent/memory/sensitivity.d.ts +3 -0
  135. package/dist/src/agent/memory/sensitivity.js +13 -0
  136. package/dist/src/agent/memory/sensitivity.js.map +1 -0
  137. package/dist/src/agent/memory/understanding/service.js +10 -14
  138. package/dist/src/agent/memory/understanding/service.js.map +1 -1
  139. package/dist/src/agent/memory/user-context-coordinator.d.ts +1 -0
  140. package/dist/src/agent/memory/user-context-coordinator.js +2 -0
  141. package/dist/src/agent/memory/user-context-coordinator.js.map +1 -1
  142. package/dist/src/agent/orchestration/agent-orchestrator.js +8 -1
  143. package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
  144. package/dist/src/agent/service/btw-query.d.ts +4 -0
  145. package/dist/src/agent/service/btw-query.js +94 -13
  146. package/dist/src/agent/service/btw-query.js.map +1 -1
  147. package/dist/src/agent/session/session-inspector.js +9 -1
  148. package/dist/src/agent/session/session-inspector.js.map +1 -1
  149. package/dist/src/agent/tools/dreaming-tool.js +3 -12
  150. package/dist/src/agent/tools/dreaming-tool.js.map +1 -1
  151. package/dist/src/agent/tools/factory.js +14 -2
  152. package/dist/src/agent/tools/factory.js.map +1 -1
  153. package/dist/src/agent/tools/memory-tool.d.ts +3 -0
  154. package/dist/src/agent/tools/memory-tool.js +6 -3
  155. package/dist/src/agent/tools/memory-tool.js.map +1 -1
  156. package/dist/src/agent/tools/session-search-tool.js +2 -2
  157. package/dist/src/agent/tools/session-search-tool.js.map +1 -1
  158. package/dist/src/agent/tools/web-extract.js +2 -2
  159. package/dist/src/agent/tools/web-extract.js.map +1 -1
  160. package/dist/src/agent/workspace-runtime/registry.d.ts +6 -6
  161. package/dist/src/agent/workspace-runtime/registry.js +19 -12
  162. package/dist/src/agent/workspace-runtime/registry.js.map +1 -1
  163. package/dist/src/agent-manifest/schema.d.ts +4 -0
  164. package/dist/src/agent-manifest/schema.js +2 -1
  165. package/dist/src/agent-manifest/schema.js.map +1 -1
  166. package/dist/src/agent-runtime/index.js +1 -1
  167. package/dist/src/agent-runtime/runtime-profile.js +1 -1
  168. package/dist/src/ai-assist/text-assist.d.ts +2 -1
  169. package/dist/src/ai-assist/text-assist.js +10 -23
  170. package/dist/src/ai-assist/text-assist.js.map +1 -1
  171. package/dist/src/auth/credentials.js +5 -2
  172. package/dist/src/auth/credentials.js.map +1 -1
  173. package/dist/src/auth/oauth/github-copilot.d.ts +3 -4
  174. package/dist/src/auth/oauth/github-copilot.js +66 -2
  175. package/dist/src/auth/oauth/github-copilot.js.map +1 -0
  176. package/dist/src/auth/oauth/index.js +2 -2
  177. package/dist/src/automations/draft/automation-draft-service.js +3 -3
  178. package/dist/src/automations/draft/automation-draft-service.js.map +1 -1
  179. package/dist/src/chat-commands/builtins/review.js +22 -5
  180. package/dist/src/chat-commands/builtins/review.js.map +1 -1
  181. package/dist/src/chat-commands/types.d.ts +4 -0
  182. package/dist/src/config/schema.d.ts +4 -0
  183. package/dist/src/gateway/hono/lib/config-payload.d.ts +2 -0
  184. package/dist/src/gateway/hono/routes/dreaming.js +28 -21
  185. package/dist/src/gateway/hono/routes/dreaming.js.map +1 -1
  186. package/dist/src/gateway/hono/routes/memory.js +16 -4
  187. package/dist/src/gateway/hono/routes/memory.js.map +1 -1
  188. package/dist/src/gateway/hono/routes/models.js +1 -0
  189. package/dist/src/gateway/hono/routes/models.js.map +1 -1
  190. package/dist/src/gateway/hono/routes/voice.js +2 -2
  191. package/dist/src/gateway/hono/routes/voice.js.map +1 -1
  192. package/dist/src/goals/goal-contract-draft.js +2 -2
  193. package/dist/src/goals/goal-contract-draft.js.map +1 -1
  194. package/dist/src/goals/goal-evidence-review.js +2 -2
  195. package/dist/src/goals/goal-evidence-review.js.map +1 -1
  196. package/dist/src/notes/service.js +2 -2
  197. package/dist/src/notes/service.js.map +1 -1
  198. package/dist/src/providers/constants.d.ts +2 -0
  199. package/dist/src/providers/constants.js +11 -0
  200. package/dist/src/providers/constants.js.map +1 -0
  201. package/dist/src/providers/extension-stream-bridge.js +1 -0
  202. package/dist/src/providers/extension-stream-bridge.js.map +1 -1
  203. package/dist/src/providers/index.d.ts +3 -3
  204. package/dist/src/providers/index.js +5 -4
  205. package/dist/src/providers/index.js.map +1 -1
  206. package/dist/src/providers/model-call.d.ts +17 -0
  207. package/dist/src/providers/model-call.js +60 -0
  208. package/dist/src/providers/model-call.js.map +1 -0
  209. package/dist/src/session/session-title.js +2 -2
  210. package/dist/src/session/session-title.js.map +1 -1
  211. package/dist/src/storage/sqlite/fts.d.ts +6 -0
  212. package/dist/src/storage/sqlite/fts.js +70 -1
  213. package/dist/src/storage/sqlite/fts.js.map +1 -1
  214. package/dist/src/storage/sqlite/memory-index-repository.js +16 -7
  215. package/dist/src/storage/sqlite/memory-index-repository.js.map +1 -1
  216. package/dist/src/storage/sqlite/memory-records-repository.d.ts +18 -0
  217. package/dist/src/storage/sqlite/memory-records-repository.js +55 -8
  218. package/dist/src/storage/sqlite/memory-records-repository.js.map +1 -1
  219. package/dist/src/voice/tts/summarize.js +2 -2
  220. package/dist/src/voice/tts/summarize.js.map +1 -1
  221. package/dist/src/workflows/draft/workflow-draft-service.js +2 -2
  222. package/dist/src/workflows/draft/workflow-draft-service.js.map +1 -1
  223. package/package.json +5 -5
  224. package/dist/gateway/static/root/assets/agents-Dl9EWnUh.js +0 -2
  225. package/dist/gateway/static/root/assets/automations-page-CFt5Ik4q.js +0 -4
  226. package/dist/gateway/static/root/assets/browser-settings-page-ColJPwer.js +0 -8
  227. package/dist/gateway/static/root/assets/channels-settings-BDLHV3Jx.js +0 -1
  228. package/dist/gateway/static/root/assets/channels-status-swr-EUwWYx8l.js +0 -1
  229. package/dist/gateway/static/root/assets/connectors-page-CA752gY8.js +0 -2
  230. package/dist/gateway/static/root/assets/goal-create-dialog-DVAHnifk.js +0 -1
  231. package/dist/gateway/static/root/assets/goal-detail-page-BuOEQZn_.js +0 -4
  232. package/dist/gateway/static/root/assets/goals-page-WV4cHvD-.js +0 -2
  233. package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-D51yogNZ.js +0 -1
  234. package/dist/gateway/static/root/assets/logs-page-CL22jleU.js +0 -7
  235. package/dist/gateway/static/root/assets/messages-DbNQuFdA.js +0 -3
  236. package/dist/gateway/static/root/assets/models-hub-panel-BkV9hDc-.js +0 -2
  237. package/dist/gateway/static/root/assets/notes-page-BK9yydrL.js +0 -1
  238. package/dist/gateway/static/root/assets/notes-workbench-Dd0v-hK2.js +0 -2
  239. package/dist/gateway/static/root/assets/project-detail-page-DSPhB3do.js +0 -1
  240. package/dist/gateway/static/root/assets/projects-page-C-fZk1f7.js +0 -1
  241. package/dist/gateway/static/root/assets/remote-access-hub-BGaVc3t7.js +0 -1
  242. package/dist/gateway/static/root/assets/sessions-page-DbnzgQOP.js +0 -1
  243. package/dist/gateway/static/root/assets/settings-advanced-gate-CuDBoz5k.js +0 -1
  244. package/dist/gateway/static/root/assets/shares-settings-06IYvwDt.js +0 -1
  245. package/dist/gateway/static/root/assets/skill-reload-api-0ODH0DXl.js +0 -1
  246. package/dist/gateway/static/root/assets/skills-page-3W2-wDyO.js +0 -2
  247. package/dist/gateway/static/root/assets/voice-api-key-field-BPn1ZEga.js +0 -1
  248. package/dist/gateway/static/root/assets/work-item-detail-page-BQGTTsz1.js +0 -1
  249. package/dist/gateway/static/root/assets/workflow-run-setup-panel-na-N4jZ6.js +0 -3
  250. package/dist/gateway/static/root/assets/workflows-page-CjeVDs4h.js +0 -18
@@ -1 +1 @@
1
- {"version":3,"file":"service.js","names":[],"sources":["../../../../../src/agent/memory/understanding/service.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\n\nimport {\n appendMemoryTraceEvent,\n attachMemoryEvidence,\n upsertKnowledgeSourceItems,\n} from '../../../storage/sqlite/index.js';\nimport { createLogger } from '../../../utils/logger.js';\nimport type {\n MemoryEvidence,\n MemoryKind,\n MemoryRecord,\n MemorySensitivity,\n MemoryWriteRequest,\n MemoryWriteResult,\n} from '../types.js';\nimport { extractExplicitUnderstandingCorrectionContent } from './correction.js';\nimport type { UnderstandingCandidate, UnderstandingReviewResult } from './types.js';\n\nconst log = createLogger('UserUnderstanding');\nconst MAX_CANDIDATE_CHARS = 600;\n\nconst EXPLICIT_PATTERNS: RegExp[] = [\n /\\b(?:please\\s+)?remember(?:\\s+that)?\\s+(.+)/i,\n /\\bkeep\\s+in\\s+mind(?:\\s+that)?\\s+(.+)/i,\n /(?:请)?记住[::\\s]*(.+)/,\n /以后(?:都)?(?:请)?[::\\s]*(.+)/,\n /下次(?:请)?[::\\s]*(.+)/,\n];\n\nfunction normalizeContent(raw: string): string {\n return raw\n .replace(/^[\"'“”‘’\\s]+|[\"'“”‘’\\s]+$/g, '')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, MAX_CANDIDATE_CHARS);\n}\n\nfunction inferKind(content: string): MemoryKind {\n if (/\\b(don't|do not|never|avoid)\\b/i.test(content) || /不要|别再|禁止|底线/.test(content)) {\n return 'boundary';\n }\n if (/\\b(prefer|preference|like)\\b/i.test(content) || /喜欢|偏好|习惯/.test(content)) {\n return 'preference';\n }\n if (/\\b(tool|command|pnpm|npm|git|ripgrep|rg)\\b/i.test(content) || /工具|命令/.test(content)) {\n return 'tool_preference';\n }\n if (/\\b(project|repo|codebase|workspace)\\b/i.test(content) || /项目|仓库|代码库/.test(content)) {\n return 'project_context';\n }\n if (/\\b(goal|plan|commit|deadline)\\b/i.test(content) || /目标|计划|承诺|截止/.test(content)) {\n return 'commitment';\n }\n if (/\\b(next time|lesson|failed|error)\\b/i.test(content) || /下次|教训|失败|错误/.test(content)) {\n return 'task_lesson';\n }\n return 'agent_note';\n}\n\nfunction inferSensitivity(content: string): UnderstandingCandidate['sensitivity'] {\n if (\n /\\b(api[_-]?key|access[_-]?key|token|password|secret|credential|private[_-]?key|bearer)\\b/i.test(content)\n || /密钥|密码|令牌|私钥/.test(content)\n || /\\b(?:sk|gh[opsu]|xox[abprs])-[a-z0-9_-]{8,}\\b/i.test(content)\n || /\\bAKIA[A-Z0-9]{12,}\\b/.test(content)\n || /-----BEGIN [A-Z ]*PRIVATE KEY-----/.test(content)\n ) {\n return 'secret';\n }\n if (\n /\\b(ssn|social security|bank account|credit card|medical record)\\b/i.test(content)\n || /身份证|银行卡|信用卡|医疗记录/.test(content)\n ) {\n return 'regulated';\n }\n return /家庭|住址|生日|健康|关系|family|address|birthday|health/i.test(content)\n ? 'personal'\n : 'normal';\n}\n\nfunction redactSensitiveText(content: string): string {\n return content\n .replace(/-----BEGIN [A-Z ]*PRIVATE KEY-----[\\s\\S]*?-----END [A-Z ]*PRIVATE KEY-----/g, '[REDACTED PRIVATE KEY]')\n .replace(/\\b(api[_-]?key|access[_-]?key|token|password|secret|credential|private[_-]?key|bearer)\\b\\s*[:=]?\\s*[^\\s,;]+/gi, '$1=[REDACTED]')\n .replace(/(密钥|密码|令牌|私钥)\\s*[::=]?\\s*[^\\s,;]+/g, '$1=[REDACTED]')\n .replace(/\\b(?:sk|gh[opsu]|xox[abprs])-[a-z0-9_-]{8,}\\b/gi, '[REDACTED TOKEN]')\n .replace(/\\bAKIA[A-Z0-9]{12,}\\b/g, '[REDACTED ACCESS KEY]');\n}\n\nconst SENSITIVITY_RANK: Record<MemorySensitivity, number> = {\n normal: 0,\n personal: 1,\n regulated: 2,\n secret: 3,\n};\n\nfunction stricterSensitivity(content: string, declared: MemorySensitivity): MemorySensitivity {\n const inferred = inferSensitivity(content);\n return SENSITIVITY_RANK[inferred] > SENSITIVITY_RANK[declared] ? inferred : declared;\n}\n\nfunction canonicalKey(kind: MemoryKind, content: string): string {\n const normalized = content\n .toLocaleLowerCase()\n .replace(/[^\\p{L}\\p{N}]+/gu, ' ')\n .trim();\n return `${kind}:${createHash('sha256').update(normalized).digest('hex').slice(0, 20)}`;\n}\n\nexport function extractExplicitUnderstandingCandidates(userContent: string): UnderstandingCandidate[] {\n const correction = extractExplicitUnderstandingCorrectionContent(userContent);\n if (correction) {\n const kind = inferKind(correction);\n return [{\n kind,\n content: correction,\n canonicalKey: canonicalKey(kind, correction),\n confidence: 0.95,\n importance: kind === 'boundary' ? 0.9 : 0.8,\n explicitness: 'explicit',\n durability: 'durable',\n sensitivity: inferSensitivity(correction),\n disclosurePolicy: kind === 'boundary' ? 'silent' : 'referenceable',\n tags: ['user-understanding', 'explicit-user-correction'],\n }];\n }\n const lines = userContent.split(/\\r?\\n/).map((line) => line.trim()).filter(Boolean);\n for (const line of lines.length > 0 ? lines : [userContent.trim()]) {\n for (const pattern of EXPLICIT_PATTERNS) {\n const content = normalizeContent(pattern.exec(line)?.[1] ?? '');\n if (content.length < 8) continue;\n const kind = inferKind(content);\n return [{\n kind,\n content,\n canonicalKey: canonicalKey(kind, content),\n confidence: 0.92,\n importance: kind === 'boundary' ? 0.9 : 0.75,\n explicitness: 'explicit',\n durability: 'durable',\n sensitivity: inferSensitivity(content),\n disclosurePolicy: kind === 'boundary' ? 'silent' : 'referenceable',\n tags: ['user-understanding', 'explicit-user-request'],\n }];\n }\n }\n return [];\n}\n\nexport interface UserUnderstandingServiceOptions {\n write: (request: MemoryWriteRequest) => Promise<MemoryWriteResult>;\n list: (canonicalKey: string) => Promise<MemoryRecord[]>;\n}\n\nexport class UserUnderstandingService {\n constructor(private readonly options: UserUnderstandingServiceOptions) {}\n\n async reviewTurn(params: {\n agentId?: string;\n userContent: string;\n assistantContent: string;\n sessionKey?: string;\n correctionTargetRecordIds?: string[];\n }): Promise<UnderstandingReviewResult> {\n const candidates = extractExplicitUnderstandingCandidates(params.userContent);\n const sourceText = `User:\\n${params.userContent.trim()}\\n\\nAssistant:\\n${params.assistantContent.trim()}`;\n const normalizedText = redactSensitiveText(sourceText);\n const contentHash = createHash('sha256').update(sourceText).digest('hex');\n const stored = upsertKnowledgeSourceItems([{\n sourceInstanceId: params.sessionKey ? `session:${params.sessionKey}` : 'session:unknown',\n externalId: contentHash,\n itemType: 'conversation_turn',\n authorRole: 'user',\n occurredAt: new Date().toISOString(),\n contentHash,\n normalizedText,\n metadata: params.sessionKey ? { sessionKey: params.sessionKey } : {},\n sensitivity: 'personal',\n retentionClass: 'bounded',\n synthesisStatus: 'pending',\n }]);\n const sourceItemId = stored.items[0]?.id;\n const result = await this.applyCandidates(candidates, {\n agentId: params.agentId,\n sessionKey: params.sessionKey,\n sourceItemId,\n sourceText: params.userContent,\n reviewSource: 'turn',\n supersedesRecordIds: params.correctionTargetRecordIds,\n });\n return { ...result, sourceItemId };\n }\n\n async applyCandidates(\n candidates: UnderstandingCandidate[],\n context: {\n agentId?: string;\n sessionKey?: string;\n sourceItemId?: string;\n sourceText?: string;\n reviewSource?: 'turn' | 'background';\n supersedesRecordIds?: string[];\n },\n ): Promise<Omit<UnderstandingReviewResult, 'sourceItemId'>> {\n const startedAt = Date.now();\n let created = 0;\n let deduplicated = 0;\n let rejected = 0;\n const recordIds: string[] = [];\n for (const candidate of candidates.slice(0, 10)) {\n const content = normalizeContent(candidate.content);\n const sensitivity = stricterSensitivity(content, candidate.sensitivity);\n if (\n content.length < 4\n || candidate.confidence < 0.55\n || sensitivity === 'secret'\n || sensitivity === 'regulated'\n ) {\n rejected += 1;\n continue;\n }\n const evidenceText = context.sourceText\n ? redactSensitiveText(context.sourceText).slice(0, MAX_CANDIDATE_CHARS)\n : undefined;\n const key = candidate.canonicalKey ?? canonicalKey(candidate.kind, content);\n const supersedesRecordId = context.supersedesRecordIds\n && [...new Set(context.supersedesRecordIds)].length === 1\n ? context.supersedesRecordIds[0]\n : undefined;\n const existing = await this.options.list(key);\n const evidence: MemoryEvidence = {\n ...(context.sourceItemId ? { sourceItemId: context.sourceItemId } : {}),\n relation: 'supports',\n ...(context.sessionKey ? { sessionKey: context.sessionKey } : {}),\n ...(evidenceText ? { sourceText: evidenceText } : {}),\n observedAt: new Date().toISOString(),\n confidence: candidate.confidence,\n };\n if (existing[0]) {\n deduplicated += 1;\n recordIds.push(existing[0].id);\n if (context.sourceItemId) {\n attachMemoryEvidence({\n recordId: existing[0].id,\n sourceItemId: context.sourceItemId,\n relation: 'supports',\n excerpt: evidenceText,\n confidence: candidate.confidence,\n });\n }\n continue;\n }\n const write = await this.options.write({\n kind: candidate.kind,\n content,\n canonicalKey: key,\n scope: context.sessionKey ? { sessionKey: context.sessionKey } : undefined,\n target: candidate.kind === 'preference' ? 'user' : 'memory',\n tags: [...new Set(['user-understanding', ...(candidate.tags ?? [])])],\n source: { provider: 'user-understanding' },\n confidence: candidate.confidence,\n status: 'candidate',\n sensitivity,\n explicitness: candidate.explicitness,\n durability: candidate.durability,\n importance: candidate.importance,\n disclosurePolicy: candidate.disclosurePolicy,\n evidence: [evidence],\n validFrom: candidate.validFrom,\n validTo: candidate.validTo,\n supersedesRecordId,\n });\n if (write.success) {\n created += 1;\n if (write.record) recordIds.push(write.record.id);\n if (write.record && context.sourceItemId) {\n attachMemoryEvidence({\n recordId: write.record.id,\n sourceItemId: context.sourceItemId,\n relation: 'supports',\n excerpt: evidenceText,\n confidence: candidate.confidence,\n });\n }\n } else {\n rejected += 1;\n log.debug({ errorMessage: write.error, canonicalKey: key }, 'Understanding candidate was not stored');\n }\n }\n try {\n appendMemoryTraceEvent({\n sessionKey: context.sessionKey,\n phase: 'understanding',\n providerId: 'user-understanding',\n request: {\n ...(context.agentId ? { agentId: context.agentId } : {}),\n source: context.reviewSource ?? 'turn',\n proposed: candidates.length,\n created,\n deduplicated,\n rejected,\n },\n resultCount: created,\n selectedRecordIds: recordIds,\n durationMs: Date.now() - startedAt,\n });\n } catch (err) {\n log.debug({ err, sessionKey: context.sessionKey }, 'Understanding quality trace append failed');\n }\n return { proposed: candidates.length, created, deduplicated, rejected };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"service.js","names":[],"sources":["../../../../../src/agent/memory/understanding/service.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\n\nimport {\n appendMemoryTraceEvent,\n attachMemoryEvidence,\n upsertKnowledgeSourceItems,\n} from '../../../storage/sqlite/index.js';\nimport { createLogger } from '../../../utils/logger.js';\nimport type {\n MemoryEvidence,\n MemoryKind,\n MemoryRecord,\n MemorySensitivity,\n MemoryWriteRequest,\n MemoryWriteResult,\n} from '../types.js';\nimport { extractExplicitUnderstandingCorrectionContent } from './correction.js';\nimport type { UnderstandingCandidate, UnderstandingReviewResult } from './types.js';\nimport { inferMemorySensitivity, redactSensitiveMemoryText } from '../sensitivity.js';\n\nconst log = createLogger('UserUnderstanding');\nconst MAX_CANDIDATE_CHARS = 600;\n\nconst EXPLICIT_PATTERNS: RegExp[] = [\n /\\b(?:please\\s+)?remember(?:\\s+that)?\\s+(.+)/i,\n /\\bkeep\\s+in\\s+mind(?:\\s+that)?\\s+(.+)/i,\n /(?:请)?记住[::\\s]*(.+)/,\n /以后(?:都)?(?:请)?[::\\s]*(.+)/,\n /下次(?:请)?[::\\s]*(.+)/,\n];\n\nfunction normalizeContent(raw: string): string {\n return raw\n .replace(/^[\"'“”‘’\\s]+|[\"'“”‘’\\s]+$/g, '')\n .replace(/\\s+/g, ' ')\n .trim()\n .slice(0, MAX_CANDIDATE_CHARS);\n}\n\nfunction inferKind(content: string): MemoryKind {\n if (/\\b(don't|do not|never|avoid)\\b/i.test(content) || /不要|别再|禁止|底线/.test(content)) {\n return 'boundary';\n }\n if (/\\b(prefer|preference|like)\\b/i.test(content) || /喜欢|偏好|习惯/.test(content)) {\n return 'preference';\n }\n if (/\\b(tool|command|pnpm|npm|git|ripgrep|rg)\\b/i.test(content) || /工具|命令/.test(content)) {\n return 'tool_preference';\n }\n if (/\\b(project|repo|codebase|workspace)\\b/i.test(content) || /项目|仓库|代码库/.test(content)) {\n return 'project_context';\n }\n if (/\\b(goal|plan|commit|deadline)\\b/i.test(content) || /目标|计划|承诺|截止/.test(content)) {\n return 'commitment';\n }\n if (/\\b(next time|lesson|failed|error)\\b/i.test(content) || /下次|教训|失败|错误/.test(content)) {\n return 'task_lesson';\n }\n return 'agent_note';\n}\n\nconst SENSITIVITY_RANK: Record<MemorySensitivity, number> = {\n normal: 0,\n personal: 1,\n regulated: 2,\n secret: 3,\n};\n\nfunction stricterSensitivity(content: string, declared: MemorySensitivity): MemorySensitivity {\n const inferred = inferMemorySensitivity(content);\n return SENSITIVITY_RANK[inferred] > SENSITIVITY_RANK[declared] ? inferred : declared;\n}\n\nfunction canonicalKey(kind: MemoryKind, content: string): string {\n const normalized = content\n .toLocaleLowerCase()\n .replace(/[^\\p{L}\\p{N}]+/gu, ' ')\n .trim();\n return `${kind}:${createHash('sha256').update(normalized).digest('hex').slice(0, 20)}`;\n}\n\nexport function extractExplicitUnderstandingCandidates(userContent: string): UnderstandingCandidate[] {\n const correction = extractExplicitUnderstandingCorrectionContent(userContent);\n if (correction) {\n const kind = inferKind(correction);\n return [{\n kind,\n content: correction,\n canonicalKey: canonicalKey(kind, correction),\n confidence: 0.95,\n importance: kind === 'boundary' ? 0.9 : 0.8,\n explicitness: 'explicit',\n durability: 'durable',\n sensitivity: inferMemorySensitivity(correction),\n disclosurePolicy: kind === 'boundary' ? 'silent' : 'referenceable',\n tags: ['user-understanding', 'explicit-user-correction'],\n }];\n }\n const lines = userContent.split(/\\r?\\n/).map((line) => line.trim()).filter(Boolean);\n for (const line of lines.length > 0 ? lines : [userContent.trim()]) {\n for (const pattern of EXPLICIT_PATTERNS) {\n const content = normalizeContent(pattern.exec(line)?.[1] ?? '');\n if (content.length < 8) continue;\n const kind = inferKind(content);\n return [{\n kind,\n content,\n canonicalKey: canonicalKey(kind, content),\n confidence: 0.92,\n importance: kind === 'boundary' ? 0.9 : 0.75,\n explicitness: 'explicit',\n durability: 'durable',\n sensitivity: inferMemorySensitivity(content),\n disclosurePolicy: kind === 'boundary' ? 'silent' : 'referenceable',\n tags: ['user-understanding', 'explicit-user-request'],\n }];\n }\n }\n return [];\n}\n\nexport interface UserUnderstandingServiceOptions {\n write: (request: MemoryWriteRequest) => Promise<MemoryWriteResult>;\n list: (canonicalKey: string) => Promise<MemoryRecord[]>;\n}\n\nexport class UserUnderstandingService {\n constructor(private readonly options: UserUnderstandingServiceOptions) {}\n\n async reviewTurn(params: {\n agentId?: string;\n userContent: string;\n assistantContent: string;\n sessionKey?: string;\n correctionTargetRecordIds?: string[];\n }): Promise<UnderstandingReviewResult> {\n const candidates = extractExplicitUnderstandingCandidates(params.userContent);\n const sourceText = `User:\\n${params.userContent.trim()}\\n\\nAssistant:\\n${params.assistantContent.trim()}`;\n const normalizedText = redactSensitiveMemoryText(sourceText);\n const contentHash = createHash('sha256').update(sourceText).digest('hex');\n const stored = upsertKnowledgeSourceItems([{\n sourceInstanceId: params.sessionKey ? `session:${params.sessionKey}` : 'session:unknown',\n externalId: contentHash,\n itemType: 'conversation_turn',\n authorRole: 'user',\n occurredAt: new Date().toISOString(),\n contentHash,\n normalizedText,\n metadata: params.sessionKey ? { sessionKey: params.sessionKey } : {},\n sensitivity: 'personal',\n retentionClass: 'bounded',\n synthesisStatus: 'pending',\n }]);\n const sourceItemId = stored.items[0]?.id;\n const result = await this.applyCandidates(candidates, {\n agentId: params.agentId,\n sessionKey: params.sessionKey,\n sourceItemId,\n sourceText: params.userContent,\n reviewSource: 'turn',\n supersedesRecordIds: params.correctionTargetRecordIds,\n });\n return { ...result, sourceItemId };\n }\n\n async applyCandidates(\n candidates: UnderstandingCandidate[],\n context: {\n agentId?: string;\n sessionKey?: string;\n sourceItemId?: string;\n sourceText?: string;\n reviewSource?: 'turn' | 'background';\n supersedesRecordIds?: string[];\n },\n ): Promise<Omit<UnderstandingReviewResult, 'sourceItemId'>> {\n const startedAt = Date.now();\n let created = 0;\n let deduplicated = 0;\n let rejected = 0;\n const recordIds: string[] = [];\n for (const candidate of candidates.slice(0, 10)) {\n const content = normalizeContent(candidate.content);\n const sensitivity = stricterSensitivity(content, candidate.sensitivity);\n if (\n content.length < 4\n || candidate.confidence < 0.55\n || sensitivity === 'secret'\n || sensitivity === 'regulated'\n ) {\n rejected += 1;\n continue;\n }\n const evidenceText = context.sourceText\n ? redactSensitiveMemoryText(context.sourceText).slice(0, MAX_CANDIDATE_CHARS)\n : undefined;\n const key = candidate.canonicalKey ?? canonicalKey(candidate.kind, content);\n const supersedesRecordId = context.supersedesRecordIds\n && [...new Set(context.supersedesRecordIds)].length === 1\n ? context.supersedesRecordIds[0]\n : undefined;\n const existing = await this.options.list(key);\n const evidence: MemoryEvidence = {\n ...(context.sourceItemId ? { sourceItemId: context.sourceItemId } : {}),\n relation: 'supports',\n ...(context.sessionKey ? { sessionKey: context.sessionKey } : {}),\n ...(evidenceText ? { sourceText: evidenceText } : {}),\n observedAt: new Date().toISOString(),\n confidence: candidate.confidence,\n };\n if (existing[0]) {\n deduplicated += 1;\n recordIds.push(existing[0].id);\n if (context.sourceItemId) {\n attachMemoryEvidence({\n recordId: existing[0].id,\n sourceItemId: context.sourceItemId,\n relation: 'supports',\n excerpt: evidenceText,\n confidence: candidate.confidence,\n });\n }\n continue;\n }\n const write = await this.options.write({\n kind: candidate.kind,\n content,\n canonicalKey: key,\n scope: context.agentId || context.sessionKey\n ? { agentId: context.agentId, sessionKey: context.sessionKey }\n : undefined,\n target: candidate.kind === 'preference' ? 'user' : 'memory',\n tags: [...new Set(['user-understanding', ...(candidate.tags ?? [])])],\n source: { provider: 'user-understanding' },\n confidence: candidate.confidence,\n status: 'candidate',\n sensitivity,\n explicitness: candidate.explicitness,\n durability: candidate.durability,\n importance: candidate.importance,\n disclosurePolicy: candidate.disclosurePolicy,\n evidence: [evidence],\n validFrom: candidate.validFrom,\n validTo: candidate.validTo,\n supersedesRecordId,\n });\n if (write.success) {\n created += 1;\n if (write.record) recordIds.push(write.record.id);\n if (write.record && context.sourceItemId) {\n attachMemoryEvidence({\n recordId: write.record.id,\n sourceItemId: context.sourceItemId,\n relation: 'supports',\n excerpt: evidenceText,\n confidence: candidate.confidence,\n });\n }\n } else {\n rejected += 1;\n log.debug({ errorMessage: write.error, canonicalKey: key }, 'Understanding candidate was not stored');\n }\n }\n try {\n appendMemoryTraceEvent({\n sessionKey: context.sessionKey,\n phase: 'understanding',\n providerId: 'user-understanding',\n request: {\n ...(context.agentId ? { agentId: context.agentId } : {}),\n source: context.reviewSource ?? 'turn',\n proposed: candidates.length,\n created,\n deduplicated,\n rejected,\n },\n resultCount: created,\n selectedRecordIds: recordIds,\n durationMs: Date.now() - startedAt,\n });\n } catch (err) {\n log.debug({ err, sessionKey: context.sessionKey }, 'Understanding quality trace append failed');\n }\n return { proposed: candidates.length, created, deduplicated, rejected };\n }\n}\n"],"mappings":"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"}
@@ -5,6 +5,7 @@ import type { MemoryManager } from './manager.js';
5
5
  export interface UserContextCoordinatorOptions {
6
6
  getConfig: () => Config | undefined;
7
7
  isEnabledForSession: (sessionKey: string) => boolean;
8
+ getAgentIdForSession: (sessionKey: string) => string;
8
9
  getMemoryManagerForSession: (sessionKey: string) => MemoryManager;
9
10
  getLastAssistantContent: (sessionKey: string) => string | null;
10
11
  }
@@ -58,6 +58,7 @@ var UserContextCoordinator = class {
58
58
  if (!shouldPlanUserContextThisTurn(config, turn)) return empty();
59
59
  return this.planner.plan({
60
60
  memoryManager: this.options.getMemoryManagerForSession(sessionKey),
61
+ agentId: this.options.getAgentIdForSession(sessionKey),
61
62
  sessionKey,
62
63
  query,
63
64
  userMessage
@@ -71,6 +72,7 @@ var UserContextCoordinator = class {
71
72
  this.correctionTargets.delete(sessionKey);
72
73
  try {
73
74
  await memoryManager.captureTurnUnderstanding(userPlainText, assistantContent, {
75
+ agentId: this.options.getAgentIdForSession(sessionKey),
74
76
  sessionId: sessionKey,
75
77
  correctionTargetRecordIds
76
78
  });
@@ -1 +1 @@
1
- {"version":3,"file":"user-context-coordinator.js","names":[],"sources":["../../../../src/agent/memory/user-context-coordinator.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { Config } from '../../config/schema.js';\nimport { setLatestMemoryInjectFeedback } from '../../storage/sqlite/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { UserContextPlanner } from './context/planner.js';\nimport type { UserContextPlan } from './context/types.js';\nimport type { MemoryManager } from './manager.js';\nimport {\n shouldPlanUserContextThisTurn,\n} from './memory-config.js';\nimport { isExplicitUnderstandingCorrection } from './understanding/correction.js';\nimport { extractAgentUserPlainText } from './user-message-text.js';\n\nconst log = createLogger('UserContextCoordinator');\n\nexport interface UserContextCoordinatorOptions {\n getConfig: () => Config | undefined;\n isEnabledForSession: (sessionKey: string) => boolean;\n getMemoryManagerForSession: (sessionKey: string) => MemoryManager;\n getLastAssistantContent: (sessionKey: string) => string | null;\n}\n\nexport class UserContextCoordinator {\n private readonly userTurn = new Map<string, number>();\n private readonly correctionTargets = new Map<string, string[]>();\n private readonly planner = new UserContextPlanner();\n\n constructor(private readonly options: UserContextCoordinatorOptions) {}\n\n forgetSession(sessionKey: string): void {\n this.userTurn.delete(sessionKey);\n this.correctionTargets.delete(sessionKey);\n }\n\n clear(): void {\n this.userTurn.clear();\n this.correctionTargets.clear();\n }\n\n async prepare(userMessage: AgentMessage, sessionKey: string): Promise<UserContextPlan> {\n const empty = (): UserContextPlan => ({\n traceId: '',\n modelMessage: userMessage,\n items: [],\n rejected: [],\n estimatedTokens: 0,\n });\n const config = this.options.getConfig();\n if (!this.options.isEnabledForSession(sessionKey)) return empty();\n const query = extractAgentUserPlainText(userMessage);\n this.correctionTargets.delete(sessionKey);\n if (isExplicitUnderstandingCorrection(query)) {\n try {\n const trace = setLatestMemoryInjectFeedback({\n sessionKey,\n requireSelectedRecords: true,\n feedback: {\n outcome: 'not_helpful',\n source: 'system',\n reason: 'detected_explicit_user_correction',\n },\n });\n if (trace?.selectedRecordIds.length) {\n this.correctionTargets.set(sessionKey, trace.selectedRecordIds);\n }\n } catch (err) {\n log.debug({ err, sessionKey }, 'Understanding correction feedback was not persisted');\n }\n }\n const turn = (this.userTurn.get(sessionKey) ?? 0) + 1;\n this.userTurn.set(sessionKey, turn);\n if (!shouldPlanUserContextThisTurn(config, turn)) return empty();\n return this.planner.plan({\n memoryManager: this.options.getMemoryManagerForSession(sessionKey),\n sessionKey,\n query,\n userMessage,\n });\n }\n\n async afterTurn(sessionKey: string, userPlainText: string): Promise<void> {\n if (!this.options.isEnabledForSession(sessionKey)) return;\n const memoryManager = this.options.getMemoryManagerForSession(sessionKey);\n const assistantContent = this.options.getLastAssistantContent(sessionKey) ?? '';\n const correctionTargetRecordIds = this.correctionTargets.get(sessionKey);\n this.correctionTargets.delete(sessionKey);\n try {\n await memoryManager.captureTurnUnderstanding(\n userPlainText,\n assistantContent,\n { sessionId: sessionKey, correctionTargetRecordIds },\n );\n } catch (err) {\n log.warn({ err, sessionKey }, 'Turn understanding capture failed');\n }\n void memoryManager.syncProvidersForTurn(userPlainText, assistantContent, { sessionId: sessionKey });\n memoryManager.queuePrefetchAll(userPlainText, { sessionId: sessionKey });\n }\n}\n"],"mappings":";;;;;;;;;aAIqD;AAUrD,MAAM,MAAM,aAAa,yBAAyB;AASlD,IAAa,yBAAb,MAAoC;CAClC,2BAA4B,IAAI,KAAqB;CACrD,oCAAqC,IAAI,KAAuB;CAChE,UAA2B,IAAI,oBAAoB;CAEnD,YAAY,SAAyD;AAAxC,OAAA,UAAA;;CAE7B,cAAc,YAA0B;AACtC,OAAK,SAAS,OAAO,WAAW;AAChC,OAAK,kBAAkB,OAAO,WAAW;;CAG3C,QAAc;AACZ,OAAK,SAAS,OAAO;AACrB,OAAK,kBAAkB,OAAO;;CAGhC,MAAM,QAAQ,aAA2B,YAA8C;EACrF,MAAM,eAAgC;GACpC,SAAS;GACT,cAAc;GACd,OAAO,EAAE;GACT,UAAU,EAAE;GACZ,iBAAiB;GAClB;EACD,MAAM,SAAS,KAAK,QAAQ,WAAW;AACvC,MAAI,CAAC,KAAK,QAAQ,oBAAoB,WAAW,CAAE,QAAO,OAAO;EACjE,MAAM,QAAQ,0BAA0B,YAAY;AACpD,OAAK,kBAAkB,OAAO,WAAW;AACzC,MAAI,kCAAkC,MAAM,CAC1C,KAAI;GACF,MAAM,QAAQ,8BAA8B;IAC1C;IACA,wBAAwB;IACxB,UAAU;KACR,SAAS;KACT,QAAQ;KACR,QAAQ;KACT;IACF,CAAC;AACF,OAAI,OAAO,kBAAkB,OAC3B,MAAK,kBAAkB,IAAI,YAAY,MAAM,kBAAkB;WAE1D,KAAK;AACZ,OAAI,MAAM;IAAE;IAAK;IAAY,EAAE,sDAAsD;;EAGzF,MAAM,QAAQ,KAAK,SAAS,IAAI,WAAW,IAAI,KAAK;AACpD,OAAK,SAAS,IAAI,YAAY,KAAK;AACnC,MAAI,CAAC,8BAA8B,QAAQ,KAAK,CAAE,QAAO,OAAO;AAChE,SAAO,KAAK,QAAQ,KAAK;GACvB,eAAe,KAAK,QAAQ,2BAA2B,WAAW;GAClE;GACA;GACA;GACD,CAAC;;CAGJ,MAAM,UAAU,YAAoB,eAAsC;AACxE,MAAI,CAAC,KAAK,QAAQ,oBAAoB,WAAW,CAAE;EACnD,MAAM,gBAAgB,KAAK,QAAQ,2BAA2B,WAAW;EACzE,MAAM,mBAAmB,KAAK,QAAQ,wBAAwB,WAAW,IAAI;EAC7E,MAAM,4BAA4B,KAAK,kBAAkB,IAAI,WAAW;AACxE,OAAK,kBAAkB,OAAO,WAAW;AACzC,MAAI;AACF,SAAM,cAAc,yBAClB,eACA,kBACA;IAAE,WAAW;IAAY;IAA2B,CACrD;WACM,KAAK;AACZ,OAAI,KAAK;IAAE;IAAK;IAAY,EAAE,oCAAoC;;AAE/D,gBAAc,qBAAqB,eAAe,kBAAkB,EAAE,WAAW,YAAY,CAAC;AACnG,gBAAc,iBAAiB,eAAe,EAAE,WAAW,YAAY,CAAC"}
1
+ {"version":3,"file":"user-context-coordinator.js","names":[],"sources":["../../../../src/agent/memory/user-context-coordinator.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { Config } from '../../config/schema.js';\nimport { setLatestMemoryInjectFeedback } from '../../storage/sqlite/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { UserContextPlanner } from './context/planner.js';\nimport type { UserContextPlan } from './context/types.js';\nimport type { MemoryManager } from './manager.js';\nimport {\n shouldPlanUserContextThisTurn,\n} from './memory-config.js';\nimport { isExplicitUnderstandingCorrection } from './understanding/correction.js';\nimport { extractAgentUserPlainText } from './user-message-text.js';\n\nconst log = createLogger('UserContextCoordinator');\n\nexport interface UserContextCoordinatorOptions {\n getConfig: () => Config | undefined;\n isEnabledForSession: (sessionKey: string) => boolean;\n getAgentIdForSession: (sessionKey: string) => string;\n getMemoryManagerForSession: (sessionKey: string) => MemoryManager;\n getLastAssistantContent: (sessionKey: string) => string | null;\n}\n\nexport class UserContextCoordinator {\n private readonly userTurn = new Map<string, number>();\n private readonly correctionTargets = new Map<string, string[]>();\n private readonly planner = new UserContextPlanner();\n\n constructor(private readonly options: UserContextCoordinatorOptions) {}\n\n forgetSession(sessionKey: string): void {\n this.userTurn.delete(sessionKey);\n this.correctionTargets.delete(sessionKey);\n }\n\n clear(): void {\n this.userTurn.clear();\n this.correctionTargets.clear();\n }\n\n async prepare(userMessage: AgentMessage, sessionKey: string): Promise<UserContextPlan> {\n const empty = (): UserContextPlan => ({\n traceId: '',\n modelMessage: userMessage,\n items: [],\n rejected: [],\n estimatedTokens: 0,\n });\n const config = this.options.getConfig();\n if (!this.options.isEnabledForSession(sessionKey)) return empty();\n const query = extractAgentUserPlainText(userMessage);\n this.correctionTargets.delete(sessionKey);\n if (isExplicitUnderstandingCorrection(query)) {\n try {\n const trace = setLatestMemoryInjectFeedback({\n sessionKey,\n requireSelectedRecords: true,\n feedback: {\n outcome: 'not_helpful',\n source: 'system',\n reason: 'detected_explicit_user_correction',\n },\n });\n if (trace?.selectedRecordIds.length) {\n this.correctionTargets.set(sessionKey, trace.selectedRecordIds);\n }\n } catch (err) {\n log.debug({ err, sessionKey }, 'Understanding correction feedback was not persisted');\n }\n }\n const turn = (this.userTurn.get(sessionKey) ?? 0) + 1;\n this.userTurn.set(sessionKey, turn);\n if (!shouldPlanUserContextThisTurn(config, turn)) return empty();\n return this.planner.plan({\n memoryManager: this.options.getMemoryManagerForSession(sessionKey),\n agentId: this.options.getAgentIdForSession(sessionKey),\n sessionKey,\n query,\n userMessage,\n });\n }\n\n async afterTurn(sessionKey: string, userPlainText: string): Promise<void> {\n if (!this.options.isEnabledForSession(sessionKey)) return;\n const memoryManager = this.options.getMemoryManagerForSession(sessionKey);\n const assistantContent = this.options.getLastAssistantContent(sessionKey) ?? '';\n const correctionTargetRecordIds = this.correctionTargets.get(sessionKey);\n this.correctionTargets.delete(sessionKey);\n try {\n await memoryManager.captureTurnUnderstanding(\n userPlainText,\n assistantContent,\n {\n agentId: this.options.getAgentIdForSession(sessionKey),\n sessionId: sessionKey,\n correctionTargetRecordIds,\n },\n );\n } catch (err) {\n log.warn({ err, sessionKey }, 'Turn understanding capture failed');\n }\n void memoryManager.syncProvidersForTurn(userPlainText, assistantContent, { sessionId: sessionKey });\n memoryManager.queuePrefetchAll(userPlainText, { sessionId: sessionKey });\n }\n}\n"],"mappings":";;;;;;;;;aAIqD;AAUrD,MAAM,MAAM,aAAa,yBAAyB;AAUlD,IAAa,yBAAb,MAAoC;CAClC,2BAA4B,IAAI,KAAqB;CACrD,oCAAqC,IAAI,KAAuB;CAChE,UAA2B,IAAI,oBAAoB;CAEnD,YAAY,SAAyD;AAAxC,OAAA,UAAA;;CAE7B,cAAc,YAA0B;AACtC,OAAK,SAAS,OAAO,WAAW;AAChC,OAAK,kBAAkB,OAAO,WAAW;;CAG3C,QAAc;AACZ,OAAK,SAAS,OAAO;AACrB,OAAK,kBAAkB,OAAO;;CAGhC,MAAM,QAAQ,aAA2B,YAA8C;EACrF,MAAM,eAAgC;GACpC,SAAS;GACT,cAAc;GACd,OAAO,EAAE;GACT,UAAU,EAAE;GACZ,iBAAiB;GAClB;EACD,MAAM,SAAS,KAAK,QAAQ,WAAW;AACvC,MAAI,CAAC,KAAK,QAAQ,oBAAoB,WAAW,CAAE,QAAO,OAAO;EACjE,MAAM,QAAQ,0BAA0B,YAAY;AACpD,OAAK,kBAAkB,OAAO,WAAW;AACzC,MAAI,kCAAkC,MAAM,CAC1C,KAAI;GACF,MAAM,QAAQ,8BAA8B;IAC1C;IACA,wBAAwB;IACxB,UAAU;KACR,SAAS;KACT,QAAQ;KACR,QAAQ;KACT;IACF,CAAC;AACF,OAAI,OAAO,kBAAkB,OAC3B,MAAK,kBAAkB,IAAI,YAAY,MAAM,kBAAkB;WAE1D,KAAK;AACZ,OAAI,MAAM;IAAE;IAAK;IAAY,EAAE,sDAAsD;;EAGzF,MAAM,QAAQ,KAAK,SAAS,IAAI,WAAW,IAAI,KAAK;AACpD,OAAK,SAAS,IAAI,YAAY,KAAK;AACnC,MAAI,CAAC,8BAA8B,QAAQ,KAAK,CAAE,QAAO,OAAO;AAChE,SAAO,KAAK,QAAQ,KAAK;GACvB,eAAe,KAAK,QAAQ,2BAA2B,WAAW;GAClE,SAAS,KAAK,QAAQ,qBAAqB,WAAW;GACtD;GACA;GACA;GACD,CAAC;;CAGJ,MAAM,UAAU,YAAoB,eAAsC;AACxE,MAAI,CAAC,KAAK,QAAQ,oBAAoB,WAAW,CAAE;EACnD,MAAM,gBAAgB,KAAK,QAAQ,2BAA2B,WAAW;EACzE,MAAM,mBAAmB,KAAK,QAAQ,wBAAwB,WAAW,IAAI;EAC7E,MAAM,4BAA4B,KAAK,kBAAkB,IAAI,WAAW;AACxE,OAAK,kBAAkB,OAAO,WAAW;AACzC,MAAI;AACF,SAAM,cAAc,yBAClB,eACA,kBACA;IACE,SAAS,KAAK,QAAQ,qBAAqB,WAAW;IACtD,WAAW;IACX;IACD,CACF;WACM,KAAK;AACZ,OAAI,KAAK;IAAE;IAAK;IAAY,EAAE,oCAAoC;;AAE/D,gBAAc,qBAAqB,eAAe,kBAAkB,EAAE,WAAW,YAAY,CAAC;AACnG,gBAAc,iBAAiB,eAAe,EAAE,WAAW,YAAY,CAAC"}
@@ -63,6 +63,7 @@ var AgentOrchestrator = class {
63
63
  const t0 = Date.now();
64
64
  if (content.includes("__xopc_memory_dreaming_light_sweep__")) {
65
65
  const result = await runLightSweep({
66
+ workspaceDir: scope.workspaceDir,
66
67
  dreamingRoot: scope.memoriesDir,
67
68
  config: resolved.phases.light
68
69
  });
@@ -79,8 +80,12 @@ var AgentOrchestrator = class {
79
80
  await appendDreamingEvent(scope.memoriesDir, event);
80
81
  } else if (content.includes("__xopc_memory_dreaming_rem_sweep__")) {
81
82
  const result = await runRemPatterns({
83
+ agentId: scope.agentId,
84
+ workspaceDir: scope.workspaceDir,
82
85
  dreamingRoot: scope.memoriesDir,
83
- config: resolved.phases.rem
86
+ config: resolved.phases.rem,
87
+ sensitiveWritePolicy: scope.memory.privacy?.sensitiveWritePolicy,
88
+ promotionWritePolicy: resolved.promotionWritePolicy.decision
84
89
  });
85
90
  const event = {
86
91
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -94,9 +99,11 @@ var AgentOrchestrator = class {
94
99
  await appendDreamingEvent(scope.memoriesDir, event);
95
100
  } else {
96
101
  const result = await runDreamingDeepPromotion({
102
+ agentId: scope.agentId,
97
103
  workspaceDir: scope.workspaceDir,
98
104
  dreamingRoot: scope.memoriesDir,
99
105
  config: resolved.phases.deep,
106
+ sensitiveWritePolicy: scope.memory.privacy?.sensitiveWritePolicy,
100
107
  memoryManager: this.agentManager.getMemoryManagerForSession(sessionKey)
101
108
  });
102
109
  const event = {
@@ -1 +1 @@
1
- {"version":3,"file":"agent-orchestrator.js","names":[],"sources":["../../../../src/agent/orchestration/agent-orchestrator.ts"],"sourcesContent":["/**\n * Agent Orchestrator - Coordinates Agent execution flow\n *\n * Manages the complete agent execution pipeline from message processing\n * to response generation.\n */\n\nimport type { Config } from '../../config/schema.js';\nimport { resolveEffectiveAgentProfileForSession } from '../../config/agent-profile.js';\nimport type { InboundMessage } from '../../infra/bus/index.js';\nimport type { SessionConfigStore, SessionStore } from '../../session/index.js';\nimport type { SessionHydrator } from '../session/index.js';\nimport { resolveEffectiveThinkingLevel } from '../../session/thinking-resolve.js';\nimport type { ThinkLevel } from '../transcript/thinking-types.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { SessionContext } from '../session/session-context.js';\nimport type { AgentEventHandler } from './agent-event-handler.js';\nimport type { FeedbackCoordinator } from '../feedback/feedback-coordinator.js';\nimport type { AgentManager } from '../agent-manager.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { extractAgentUserPlainText } from '../memory/user-message-text.js';\nimport { abortEmbeddedRun } from '../embedded/runs.js';\nimport { runEmbeddedTurnForSession } from '../embedded/run-for-session.js';\nimport type { EmbeddedStreamEvent } from '../embedded/types.js';\nimport { persistInboundAttachments } from '../../channels/attachments/inbound-persist.js';\nimport {\n buildTranscriptUserMessage,\n clearPendingTranscriptUserMessage,\n hydrateUserTurnForLlm,\n setPendingTranscriptUserMessage,\n} from '../inbound/attachment-pipeline.js';\nimport {\n DREAMING_SWEEP_TOKEN,\n DREAMING_LIGHT_SWEEP_TOKEN,\n DREAMING_REM_SWEEP_TOKEN,\n} from '../memory/dreaming/constants.js';\nimport { runDreamingDeepPromotion } from '../memory/dreaming/deep-promotion.js';\nimport { appendDreamingEvent, type DreamingEvent } from '../memory/dreaming/events.js';\nimport { runLightSweep } from '../memory/dreaming/light-sweep.js';\nimport { runRemPatterns } from '../memory/dreaming/rem-patterns.js';\nimport { resolveDreamingAgentScope } from '../memory/dreaming/scope.js';\n\nconst log = createLogger('AgentOrchestrator');\n\nexport interface AgentOrchestratorConfig {\n agentManager: AgentManager;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n eventHandler: AgentEventHandler;\n feedbackCoordinator: FeedbackCoordinator;\n sessionConfigStore: SessionConfigStore;\n /** Per-session hydration (workspace override + model override) before the agent runs. */\n sessionHydrator: SessionHydrator;\n getThinkingDefault: () => ThinkLevel | undefined;\n /** Per-session default from merged `agents.list` / defaults (optional). */\n getThinkingDefaultForSession?: (sessionKey: string) => ThinkLevel | undefined;\n /** Default workspace root when no per-session resolver is set. */\n workspaceRoot: string;\n /** Per-agent workspace root for attachments (optional; defaults to `workspaceRoot`). */\n getWorkspaceRootForSession?: (sessionKey: string) => string;\n /** Agent home (`…/agents/<id>/`) for inbound/TTS files — keeps internal state out of the markdown workspace. */\n getAgentInternalStorageRootForSession?: (sessionKey: string) => string;\n /** Fire-and-forget after full session persist (e.g. LLM session title); not called from mid-turn snapshots. */\n enqueueAutoTitle?: (sessionKey: string) => void;\n /** For per-turn timeout. */\n getConfig?: () => Config | undefined;\n /** Channel streaming: token/tool events from pi embedded session. */\n onEmbeddedStreamEvent?: (sessionKey: string, event: EmbeddedStreamEvent) => void;\n /** Called after a successful embedded turn with assistant plain text. */\n onEmbeddedTurnComplete?: (sessionKey: string, lastAssistantText?: string) => void;\n}\n\nexport class AgentOrchestrator {\n private agentManager: AgentManager;\n private sessionStore: SessionStore;\n private modelManager: ModelManager;\n private feedbackCoordinator: FeedbackCoordinator;\n private sessionConfigStore: SessionConfigStore;\n private sessionHydrator: SessionHydrator;\n private getThinkingDefault: () => ThinkLevel | undefined;\n private getThinkingDefaultForSession?: (sessionKey: string) => ThinkLevel | undefined;\n private enqueueAutoTitle?: (sessionKey: string) => void;\n private getConfig?: () => Config | undefined;\n private onEmbeddedStreamEvent?: (sessionKey: string, event: EmbeddedStreamEvent) => void;\n private onEmbeddedTurnComplete?: (sessionKey: string, lastAssistantText?: string) => void;\n\n constructor(config: AgentOrchestratorConfig) {\n this.agentManager = config.agentManager;\n this.sessionStore = config.sessionStore;\n this.modelManager = config.modelManager;\n this.feedbackCoordinator = config.feedbackCoordinator;\n this.sessionConfigStore = config.sessionConfigStore;\n this.sessionHydrator = config.sessionHydrator;\n this.getThinkingDefault = config.getThinkingDefault;\n this.getThinkingDefaultForSession = config.getThinkingDefaultForSession;\n this.enqueueAutoTitle = config.enqueueAutoTitle;\n this.getConfig = config.getConfig;\n this.onEmbeddedStreamEvent = config.onEmbeddedStreamEvent;\n this.onEmbeddedTurnComplete = config.onEmbeddedTurnComplete;\n }\n\n /**\n * Process a message through the agent orchestration pipeline\n */\n async process(msg: InboundMessage, context: SessionContext): Promise<void> {\n const { sessionKey } = context;\n\n log.debug({ sessionKey }, 'Processing message through agent orchestrator');\n\n await this.sessionHydrator.workspace(sessionKey);\n\n // Dreaming: short-circuit scheduled maintenance tokens into local runs.\n // This avoids spending LLM tokens for scheduled memory consolidation.\n if (\n typeof msg.content === 'string' &&\n (\n sessionKey.startsWith('cron:') ||\n sessionKey.includes(':cron:') ||\n sessionKey.includes(':automation:') ||\n context.channel === 'cron' ||\n context.channel === 'automation'\n )\n ) {\n const content = msg.content;\n const isDreamingSweep =\n content.includes(DREAMING_SWEEP_TOKEN) ||\n content.includes(DREAMING_LIGHT_SWEEP_TOKEN) ||\n content.includes(DREAMING_REM_SWEEP_TOKEN);\n\n if (isDreamingSweep) {\n const cfg = this.getConfig?.();\n if (!cfg) {\n log.warn({ sessionKey }, 'Dreaming sweep skipped: config unavailable');\n return;\n }\n const profile = resolveEffectiveAgentProfileForSession(cfg, sessionKey);\n const scope = resolveDreamingAgentScope(cfg, profile.agentId);\n const resolved = scope.config;\n const t0 = Date.now();\n\n if (content.includes(DREAMING_LIGHT_SWEEP_TOKEN)) {\n const result = await runLightSweep({ dreamingRoot: scope.memoriesDir, config: resolved.phases.light });\n const event: DreamingEvent = {\n timestamp: new Date().toISOString(), phase: 'light',\n ok: result.ok, reason: result.reason, durationMs: Date.now() - t0,\n scannedEntries: result.scannedEntries, newSignals: result.newSignals, deduped: result.deduped,\n };\n await appendDreamingEvent(scope.memoriesDir, event);\n } else if (content.includes(DREAMING_REM_SWEEP_TOKEN)) {\n const result = await runRemPatterns({ dreamingRoot: scope.memoriesDir, config: resolved.phases.rem });\n const event: DreamingEvent = {\n timestamp: new Date().toISOString(), phase: 'rem',\n ok: result.ok, reason: result.reason, durationMs: Date.now() - t0,\n patternsDiscovered: result.patternsDiscovered, entriesAnalyzed: result.entriesAnalyzed,\n };\n await appendDreamingEvent(scope.memoriesDir, event);\n } else {\n const result = await runDreamingDeepPromotion({\n workspaceDir: scope.workspaceDir,\n dreamingRoot: scope.memoriesDir,\n config: resolved.phases.deep,\n memoryManager: this.agentManager.getMemoryManagerForSession(sessionKey),\n });\n const event: DreamingEvent = {\n timestamp: new Date().toISOString(), phase: 'deep',\n ok: result.ok, reason: result.reason, durationMs: Date.now() - t0,\n candidates: result.candidates, applied: result.applied,\n };\n await appendDreamingEvent(scope.memoriesDir, event);\n }\n return;\n }\n }\n\n try {\n await this.sessionHydrator.model(sessionKey);\n\n const channelSystemPrompt =\n typeof context.metadata?.channelSystemPrompt === 'string'\n ? context.metadata.channelSystemPrompt.trim()\n : '';\n if (channelSystemPrompt) {\n this.agentManager.getOrCreateAgent(sessionKey);\n this.agentManager.applyTurnChannelSystemPrompt(sessionKey, channelSystemPrompt);\n }\n\n const thinkingDefault =\n this.getThinkingDefaultForSession?.(sessionKey) ?? this.getThinkingDefault();\n const thinkingLevel = await resolveEffectiveThinkingLevel(\n this.sessionConfigStore,\n sessionKey,\n null,\n thinkingDefault,\n );\n this.agentManager.setThinkingLevel(sessionKey, thinkingLevel);\n\n const persistedAttachments = await persistInboundAttachments(msg.attachments);\n const modelRef = this.modelManager.getModelForSession(sessionKey);\n const userMessage = await buildTranscriptUserMessage({\n text: msg.content,\n prepared: persistedAttachments,\n sessionKey,\n modelRef,\n config: this.getConfig?.(),\n agentManager: this.agentManager,\n });\n setPendingTranscriptUserMessage(sessionKey, userMessage);\n\n const userPlainForMemory = extractAgentUserPlainText(userMessage);\n const userContext = await this.agentManager.prepareUserTurnContext(\n userMessage,\n sessionKey,\n );\n const userMessageForModel = userContext.modelMessage;\n\n const llmTurn = await hydrateUserTurnForLlm({\n message: userMessage,\n modelRef,\n });\n\n this.feedbackCoordinator.startTask();\n\n const turnResult = await (async () => {\n try {\n return await runEmbeddedTurnForSession({\n sessionKey,\n userMessage: userMessageForModel,\n llmImages: llmTurn.images,\n sessionStore: this.sessionStore,\n agentManager: this.agentManager,\n modelManager: this.modelManager,\n thinkingOverride: thinkingLevel,\n getConfig: this.getConfig,\n beforeTurn: () => this.agentManager.beginBackgroundReviewUserTurn(sessionKey),\n onEvent: (event) => this.onEmbeddedStreamEvent?.(sessionKey, event),\n });\n } finally {\n clearPendingTranscriptUserMessage(sessionKey, userMessage);\n }\n })();\n\n await this.agentManager.afterAgentTurn(sessionKey, userPlainForMemory);\n this.agentManager.scheduleBackgroundReviewAfterUserTurn(sessionKey);\n\n if (turnResult.ok) {\n this.onEmbeddedTurnComplete?.(sessionKey, turnResult.lastAssistantText);\n this.enqueueAutoTitle?.(sessionKey);\n } else if (turnResult.errorMessage) {\n log.warn({ sessionKey, errorMessage: turnResult.errorMessage }, 'Embedded inbound turn failed');\n }\n\n this.feedbackCoordinator.endTask();\n\n } catch (error) {\n log.error({ err: error, sessionKey }, 'Error in agent orchestration');\n this.feedbackCoordinator.endTask();\n throw error;\n }\n }\n\n /**\n * Get the current agent model ID\n */\n getCurrentModel(): string {\n return this.modelManager.getCurrentModel();\n }\n\n /**\n * Check if agent is currently processing for a session\n */\n isProcessing(sessionKey: string): boolean {\n const agent = this.agentManager.getAgent(sessionKey);\n if (!agent) {\n return false;\n }\n return agent.state.messages.length > 0;\n }\n\n /**\n * Abort current agent execution for a session\n */\n abort(sessionKey: string): void {\n void abortEmbeddedRun(sessionKey);\n const agent = this.agentManager.getAgent(sessionKey);\n agent?.abort();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"agent-orchestrator.js","names":[],"sources":["../../../../src/agent/orchestration/agent-orchestrator.ts"],"sourcesContent":["/**\n * Agent Orchestrator - Coordinates Agent execution flow\n *\n * Manages the complete agent execution pipeline from message processing\n * to response generation.\n */\n\nimport type { Config } from '../../config/schema.js';\nimport { resolveEffectiveAgentProfileForSession } from '../../config/agent-profile.js';\nimport type { InboundMessage } from '../../infra/bus/index.js';\nimport type { SessionConfigStore, SessionStore } from '../../session/index.js';\nimport type { SessionHydrator } from '../session/index.js';\nimport { resolveEffectiveThinkingLevel } from '../../session/thinking-resolve.js';\nimport type { ThinkLevel } from '../transcript/thinking-types.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { SessionContext } from '../session/session-context.js';\nimport type { AgentEventHandler } from './agent-event-handler.js';\nimport type { FeedbackCoordinator } from '../feedback/feedback-coordinator.js';\nimport type { AgentManager } from '../agent-manager.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { extractAgentUserPlainText } from '../memory/user-message-text.js';\nimport { abortEmbeddedRun } from '../embedded/runs.js';\nimport { runEmbeddedTurnForSession } from '../embedded/run-for-session.js';\nimport type { EmbeddedStreamEvent } from '../embedded/types.js';\nimport { persistInboundAttachments } from '../../channels/attachments/inbound-persist.js';\nimport {\n buildTranscriptUserMessage,\n clearPendingTranscriptUserMessage,\n hydrateUserTurnForLlm,\n setPendingTranscriptUserMessage,\n} from '../inbound/attachment-pipeline.js';\nimport {\n DREAMING_SWEEP_TOKEN,\n DREAMING_LIGHT_SWEEP_TOKEN,\n DREAMING_REM_SWEEP_TOKEN,\n} from '../memory/dreaming/constants.js';\nimport { runDreamingDeepPromotion } from '../memory/dreaming/deep-promotion.js';\nimport { appendDreamingEvent, type DreamingEvent } from '../memory/dreaming/events.js';\nimport { runLightSweep } from '../memory/dreaming/light-sweep.js';\nimport { runRemPatterns } from '../memory/dreaming/rem-patterns.js';\nimport { resolveDreamingAgentScope } from '../memory/dreaming/scope.js';\n\nconst log = createLogger('AgentOrchestrator');\n\nexport interface AgentOrchestratorConfig {\n agentManager: AgentManager;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n eventHandler: AgentEventHandler;\n feedbackCoordinator: FeedbackCoordinator;\n sessionConfigStore: SessionConfigStore;\n /** Per-session hydration (workspace override + model override) before the agent runs. */\n sessionHydrator: SessionHydrator;\n getThinkingDefault: () => ThinkLevel | undefined;\n /** Per-session default from merged `agents.list` / defaults (optional). */\n getThinkingDefaultForSession?: (sessionKey: string) => ThinkLevel | undefined;\n /** Default workspace root when no per-session resolver is set. */\n workspaceRoot: string;\n /** Per-agent workspace root for attachments (optional; defaults to `workspaceRoot`). */\n getWorkspaceRootForSession?: (sessionKey: string) => string;\n /** Agent home (`…/agents/<id>/`) for inbound/TTS files — keeps internal state out of the markdown workspace. */\n getAgentInternalStorageRootForSession?: (sessionKey: string) => string;\n /** Fire-and-forget after full session persist (e.g. LLM session title); not called from mid-turn snapshots. */\n enqueueAutoTitle?: (sessionKey: string) => void;\n /** For per-turn timeout. */\n getConfig?: () => Config | undefined;\n /** Channel streaming: token/tool events from pi embedded session. */\n onEmbeddedStreamEvent?: (sessionKey: string, event: EmbeddedStreamEvent) => void;\n /** Called after a successful embedded turn with assistant plain text. */\n onEmbeddedTurnComplete?: (sessionKey: string, lastAssistantText?: string) => void;\n}\n\nexport class AgentOrchestrator {\n private agentManager: AgentManager;\n private sessionStore: SessionStore;\n private modelManager: ModelManager;\n private feedbackCoordinator: FeedbackCoordinator;\n private sessionConfigStore: SessionConfigStore;\n private sessionHydrator: SessionHydrator;\n private getThinkingDefault: () => ThinkLevel | undefined;\n private getThinkingDefaultForSession?: (sessionKey: string) => ThinkLevel | undefined;\n private enqueueAutoTitle?: (sessionKey: string) => void;\n private getConfig?: () => Config | undefined;\n private onEmbeddedStreamEvent?: (sessionKey: string, event: EmbeddedStreamEvent) => void;\n private onEmbeddedTurnComplete?: (sessionKey: string, lastAssistantText?: string) => void;\n\n constructor(config: AgentOrchestratorConfig) {\n this.agentManager = config.agentManager;\n this.sessionStore = config.sessionStore;\n this.modelManager = config.modelManager;\n this.feedbackCoordinator = config.feedbackCoordinator;\n this.sessionConfigStore = config.sessionConfigStore;\n this.sessionHydrator = config.sessionHydrator;\n this.getThinkingDefault = config.getThinkingDefault;\n this.getThinkingDefaultForSession = config.getThinkingDefaultForSession;\n this.enqueueAutoTitle = config.enqueueAutoTitle;\n this.getConfig = config.getConfig;\n this.onEmbeddedStreamEvent = config.onEmbeddedStreamEvent;\n this.onEmbeddedTurnComplete = config.onEmbeddedTurnComplete;\n }\n\n /**\n * Process a message through the agent orchestration pipeline\n */\n async process(msg: InboundMessage, context: SessionContext): Promise<void> {\n const { sessionKey } = context;\n\n log.debug({ sessionKey }, 'Processing message through agent orchestrator');\n\n await this.sessionHydrator.workspace(sessionKey);\n\n // Dreaming: short-circuit scheduled maintenance tokens into local runs.\n // This avoids spending LLM tokens for scheduled memory consolidation.\n if (\n typeof msg.content === 'string' &&\n (\n sessionKey.startsWith('cron:') ||\n sessionKey.includes(':cron:') ||\n sessionKey.includes(':automation:') ||\n context.channel === 'cron' ||\n context.channel === 'automation'\n )\n ) {\n const content = msg.content;\n const isDreamingSweep =\n content.includes(DREAMING_SWEEP_TOKEN) ||\n content.includes(DREAMING_LIGHT_SWEEP_TOKEN) ||\n content.includes(DREAMING_REM_SWEEP_TOKEN);\n\n if (isDreamingSweep) {\n const cfg = this.getConfig?.();\n if (!cfg) {\n log.warn({ sessionKey }, 'Dreaming sweep skipped: config unavailable');\n return;\n }\n const profile = resolveEffectiveAgentProfileForSession(cfg, sessionKey);\n const scope = resolveDreamingAgentScope(cfg, profile.agentId);\n const resolved = scope.config;\n const t0 = Date.now();\n\n if (content.includes(DREAMING_LIGHT_SWEEP_TOKEN)) {\n const result = await runLightSweep({\n workspaceDir: scope.workspaceDir,\n dreamingRoot: scope.memoriesDir,\n config: resolved.phases.light,\n });\n const event: DreamingEvent = {\n timestamp: new Date().toISOString(), phase: 'light',\n ok: result.ok, reason: result.reason, durationMs: Date.now() - t0,\n scannedEntries: result.scannedEntries, newSignals: result.newSignals, deduped: result.deduped,\n };\n await appendDreamingEvent(scope.memoriesDir, event);\n } else if (content.includes(DREAMING_REM_SWEEP_TOKEN)) {\n const result = await runRemPatterns({\n agentId: scope.agentId,\n workspaceDir: scope.workspaceDir,\n dreamingRoot: scope.memoriesDir,\n config: resolved.phases.rem,\n sensitiveWritePolicy: scope.memory.privacy?.sensitiveWritePolicy,\n promotionWritePolicy: resolved.promotionWritePolicy.decision,\n });\n const event: DreamingEvent = {\n timestamp: new Date().toISOString(), phase: 'rem',\n ok: result.ok, reason: result.reason, durationMs: Date.now() - t0,\n patternsDiscovered: result.patternsDiscovered, entriesAnalyzed: result.entriesAnalyzed,\n };\n await appendDreamingEvent(scope.memoriesDir, event);\n } else {\n const result = await runDreamingDeepPromotion({\n agentId: scope.agentId,\n workspaceDir: scope.workspaceDir,\n dreamingRoot: scope.memoriesDir,\n config: resolved.phases.deep,\n sensitiveWritePolicy: scope.memory.privacy?.sensitiveWritePolicy,\n memoryManager: this.agentManager.getMemoryManagerForSession(sessionKey),\n });\n const event: DreamingEvent = {\n timestamp: new Date().toISOString(), phase: 'deep',\n ok: result.ok, reason: result.reason, durationMs: Date.now() - t0,\n candidates: result.candidates, applied: result.applied,\n };\n await appendDreamingEvent(scope.memoriesDir, event);\n }\n return;\n }\n }\n\n try {\n await this.sessionHydrator.model(sessionKey);\n\n const channelSystemPrompt =\n typeof context.metadata?.channelSystemPrompt === 'string'\n ? context.metadata.channelSystemPrompt.trim()\n : '';\n if (channelSystemPrompt) {\n this.agentManager.getOrCreateAgent(sessionKey);\n this.agentManager.applyTurnChannelSystemPrompt(sessionKey, channelSystemPrompt);\n }\n\n const thinkingDefault =\n this.getThinkingDefaultForSession?.(sessionKey) ?? this.getThinkingDefault();\n const thinkingLevel = await resolveEffectiveThinkingLevel(\n this.sessionConfigStore,\n sessionKey,\n null,\n thinkingDefault,\n );\n this.agentManager.setThinkingLevel(sessionKey, thinkingLevel);\n\n const persistedAttachments = await persistInboundAttachments(msg.attachments);\n const modelRef = this.modelManager.getModelForSession(sessionKey);\n const userMessage = await buildTranscriptUserMessage({\n text: msg.content,\n prepared: persistedAttachments,\n sessionKey,\n modelRef,\n config: this.getConfig?.(),\n agentManager: this.agentManager,\n });\n setPendingTranscriptUserMessage(sessionKey, userMessage);\n\n const userPlainForMemory = extractAgentUserPlainText(userMessage);\n const userContext = await this.agentManager.prepareUserTurnContext(\n userMessage,\n sessionKey,\n );\n const userMessageForModel = userContext.modelMessage;\n\n const llmTurn = await hydrateUserTurnForLlm({\n message: userMessage,\n modelRef,\n });\n\n this.feedbackCoordinator.startTask();\n\n const turnResult = await (async () => {\n try {\n return await runEmbeddedTurnForSession({\n sessionKey,\n userMessage: userMessageForModel,\n llmImages: llmTurn.images,\n sessionStore: this.sessionStore,\n agentManager: this.agentManager,\n modelManager: this.modelManager,\n thinkingOverride: thinkingLevel,\n getConfig: this.getConfig,\n beforeTurn: () => this.agentManager.beginBackgroundReviewUserTurn(sessionKey),\n onEvent: (event) => this.onEmbeddedStreamEvent?.(sessionKey, event),\n });\n } finally {\n clearPendingTranscriptUserMessage(sessionKey, userMessage);\n }\n })();\n\n await this.agentManager.afterAgentTurn(sessionKey, userPlainForMemory);\n this.agentManager.scheduleBackgroundReviewAfterUserTurn(sessionKey);\n\n if (turnResult.ok) {\n this.onEmbeddedTurnComplete?.(sessionKey, turnResult.lastAssistantText);\n this.enqueueAutoTitle?.(sessionKey);\n } else if (turnResult.errorMessage) {\n log.warn({ sessionKey, errorMessage: turnResult.errorMessage }, 'Embedded inbound turn failed');\n }\n\n this.feedbackCoordinator.endTask();\n\n } catch (error) {\n log.error({ err: error, sessionKey }, 'Error in agent orchestration');\n this.feedbackCoordinator.endTask();\n throw error;\n }\n }\n\n /**\n * Get the current agent model ID\n */\n getCurrentModel(): string {\n return this.modelManager.getCurrentModel();\n }\n\n /**\n * Check if agent is currently processing for a session\n */\n isProcessing(sessionKey: string): boolean {\n const agent = this.agentManager.getAgent(sessionKey);\n if (!agent) {\n return false;\n }\n return agent.state.messages.length > 0;\n }\n\n /**\n * Abort current agent execution for a session\n */\n abort(sessionKey: string): void {\n void abortEmbeddedRun(sessionKey);\n const agent = this.agentManager.getAgent(sessionKey);\n agent?.abort();\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import type { SessionStore } from '../../session/index.js';
2
+ import type { CredentialResolverOptions } from '../../auth/credentials.js';
2
3
  type BtwLog = {
3
4
  warn: (obj: Record<string, unknown>, msg: string) => void;
4
5
  };
@@ -11,6 +12,9 @@ export declare function runBtwQuery(opts: {
11
12
  log: BtwLog;
12
13
  maxTokens?: number;
13
14
  temperature?: number;
15
+ includeSessionContext?: boolean;
16
+ onTextDelta?: (delta: string) => void | Promise<void>;
17
+ credentialOptions?: CredentialResolverOptions;
14
18
  }): Promise<{
15
19
  text: string;
16
20
  error?: string;
@@ -1,6 +1,6 @@
1
1
  import { init_providers, resolveModel } from "../../providers/index.js";
2
+ import { completeWithResolvedCredentials, createResolvedModelStream, resolveModelCallApiKey } from "../../providers/model-call.js";
2
3
  import { readAgentMessageContent } from "../memory/agent-message-access.js";
3
- import { complete } from "@earendil-works/pi-ai/compat";
4
4
  //#region src/agent/service/btw-query.ts
5
5
  init_providers();
6
6
  function textFromCompleteContent(content) {
@@ -26,7 +26,6 @@ async function runBtwQuery(opts) {
26
26
  text: "",
27
27
  error: "Empty question."
28
28
  };
29
- const messages = await opts.sessionStore.load(opts.sessionKey);
30
29
  const modelRef = opts.modelForSession;
31
30
  let model;
32
31
  try {
@@ -43,10 +42,37 @@ async function runBtwQuery(opts) {
43
42
  error: `Could not resolve model: ${modelRef}`
44
43
  };
45
44
  }
46
- const background = formatMessagesForBtw(messages.slice(-40));
47
- const userMessage = {
48
- role: "user",
49
- content: [
45
+ let apiKey;
46
+ try {
47
+ apiKey = await resolveModelCallApiKey(model, opts.credentialOptions);
48
+ } catch (err) {
49
+ const em = err instanceof Error ? err.message : String(err);
50
+ opts.log.warn({
51
+ err,
52
+ modelRef,
53
+ provider: model.provider,
54
+ errorMessage: em
55
+ }, "btwQuery: credential lookup failed");
56
+ return {
57
+ text: "",
58
+ error: `Could not load credentials for provider: ${model.provider}`
59
+ };
60
+ }
61
+ if (!apiKey) {
62
+ opts.log.warn({
63
+ modelRef,
64
+ provider: model.provider
65
+ }, "btwQuery: provider credentials are not configured");
66
+ return {
67
+ text: "",
68
+ error: `No API key for provider: ${model.provider}`
69
+ };
70
+ }
71
+ const includeSessionContext = opts.includeSessionContext ?? true;
72
+ let userPrompt = q;
73
+ if (includeSessionContext) {
74
+ const background = formatMessagesForBtw((await opts.sessionStore.load(opts.sessionKey)).slice(-40));
75
+ userPrompt = [
50
76
  [
51
77
  "You are answering an ephemeral /btw side question about the current conversation.",
52
78
  "Use the conversation only as background context.",
@@ -62,7 +88,11 @@ async function runBtwQuery(opts) {
62
88
  "---",
63
89
  "Side question:",
64
90
  q
65
- ].join("\n"),
91
+ ].join("\n");
92
+ }
93
+ const userMessage = {
94
+ role: "user",
95
+ content: userPrompt,
66
96
  timestamp: Date.now()
67
97
  };
68
98
  const controller = new AbortController();
@@ -70,14 +100,65 @@ async function runBtwQuery(opts) {
70
100
  try {
71
101
  const requestedMaxTokens = Math.max(1, Math.trunc(opts.maxTokens ?? 2048));
72
102
  const modelMaxTokens = typeof model.maxTokens === "number" && Number.isFinite(model.maxTokens) ? Math.max(1, Math.trunc(model.maxTokens)) : void 0;
73
- const text = textFromCompleteContent((await complete(model, { messages: [userMessage] }, {
103
+ const modelCallOptions = {
74
104
  maxTokens: modelMaxTokens ? Math.min(requestedMaxTokens, modelMaxTokens) : requestedMaxTokens,
75
- temperature: opts.temperature ?? .4,
76
- signal: controller.signal
77
- })).content).trim();
78
- return text ? { text } : {
105
+ signal: controller.signal,
106
+ apiKey,
107
+ ...opts.temperature !== void 0 ? { temperature: opts.temperature } : {}
108
+ };
109
+ let streamedText = "";
110
+ const response = opts.onTextDelta ? await (async () => {
111
+ const stream = await createResolvedModelStream(model, { messages: [userMessage] }, modelCallOptions, opts.credentialOptions);
112
+ for await (const event of stream) {
113
+ const streamEvent = event;
114
+ if (streamEvent.type === "text_delta" && typeof streamEvent.delta === "string") {
115
+ streamedText += streamEvent.delta;
116
+ await opts.onTextDelta?.(streamEvent.delta);
117
+ continue;
118
+ }
119
+ if (streamEvent.type === "error") {
120
+ const error = streamEvent.error;
121
+ const message = error && typeof error === "object" && "errorMessage" in error ? error.errorMessage : void 0;
122
+ throw new Error(typeof message === "string" && message ? message : "Model stream failed");
123
+ }
124
+ }
125
+ return await stream.result();
126
+ })() : await completeWithResolvedCredentials(model, { messages: [userMessage] }, modelCallOptions, opts.credentialOptions);
127
+ const text = (streamedText || textFromCompleteContent(response.content)).trim();
128
+ if (text) return { text };
129
+ const stopReason = typeof response.stopReason === "string" ? response.stopReason : void 0;
130
+ const errorMessage = typeof response.errorMessage === "string" ? response.errorMessage.trim() : "";
131
+ if (stopReason === "error" && errorMessage) {
132
+ opts.log.warn({
133
+ sessionKey: opts.sessionKey,
134
+ modelRef,
135
+ stopReason,
136
+ errorMessage
137
+ }, "btwQuery model call failed");
138
+ return {
139
+ text: "",
140
+ error: errorMessage
141
+ };
142
+ }
143
+ const contentTypes = Array.isArray(response.content) ? response.content.map((block) => block && typeof block === "object" && "type" in block ? String(block.type) : "unknown").join(", ") : void 0;
144
+ const reasoningTokens = typeof response.usage?.reasoning === "number" ? response.usage.reasoning : void 0;
145
+ const detail = [
146
+ stopReason ? `stop reason: ${stopReason}` : "",
147
+ contentTypes ? `content: ${contentTypes}` : "",
148
+ reasoningTokens != null ? `reasoning tokens: ${reasoningTokens}` : ""
149
+ ].filter(Boolean).join("; ");
150
+ const error = `No text returned from model${detail ? ` (${detail})` : ""}.`;
151
+ opts.log.warn({
152
+ sessionKey: opts.sessionKey,
153
+ modelRef,
154
+ stopReason,
155
+ errorMessage: errorMessage || void 0,
156
+ contentTypes,
157
+ reasoningTokens
158
+ }, "btwQuery returned no text");
159
+ return {
79
160
  text: "",
80
- error: "No text returned from model."
161
+ error
81
162
  };
82
163
  } catch (err) {
83
164
  const em = err instanceof Error ? err.message : String(err);
@@ -1 +1 @@
1
- {"version":3,"file":"btw-query.js","names":[],"sources":["../../../../src/agent/service/btw-query.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport { complete, type UserMessage } from '@earendil-works/pi-ai/compat';\n\nimport { readAgentMessageContent } from '../memory/agent-message-access.js';\nimport type { SessionStore } from '../../session/index.js';\nimport { resolveModel } from '../../providers/index.js';\n\ntype BtwLog = { warn: (obj: Record<string, unknown>, msg: string) => void };\n\nfunction textFromCompleteContent(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n if (!Array.isArray(content)) {\n return '';\n }\n return content\n .filter(\n (c): c is { type: 'text'; text: string } =>\n c?.type === 'text' && typeof (c as { text?: unknown }).text === 'string',\n )\n .map((c) => c.text || '')\n .join('');\n}\n\nfunction formatMessagesForBtw(messages: AgentMessage[]): string {\n return messages\n .map((m) => {\n const role = m.role;\n let body = '';\n const raw = readAgentMessageContent(m);\n if (typeof raw === 'string') {\n body = raw;\n } else if (Array.isArray(raw)) {\n body = raw\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text')\n .map((c) => c.text || '')\n .join('\\n');\n }\n const line = `[${role}]: ${body}`;\n return line.length > 4000 ? `${line.slice(0, 4000)}…` : line;\n })\n .join('\\n\\n');\n}\n\n/** One-shot LLM answer for /btw: transcript as background only; does not persist. */\nexport async function runBtwQuery(opts: {\n sessionKey: string;\n question: string;\n sessionStore: SessionStore;\n modelForSession: string;\n log: BtwLog;\n maxTokens?: number;\n temperature?: number;\n}): Promise<{ text: string; error?: string }> {\n const q = opts.question.trim();\n if (!q) {\n return { text: '', error: 'Empty question.' };\n }\n const messages = await opts.sessionStore.load(opts.sessionKey);\n const modelRef = opts.modelForSession;\n let model;\n try {\n model = resolveModel(modelRef);\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n opts.log.warn({ err, modelRef, errorMessage: em }, 'btwQuery: model resolve failed');\n return { text: '', error: `Could not resolve model: ${modelRef}` };\n }\n\n const background = formatMessagesForBtw(messages.slice(-40));\n const systemBlock = [\n 'You are answering an ephemeral /btw side question about the current conversation.',\n 'Use the conversation only as background context.',\n 'Answer only the side question. Do not continue or complete any unfinished task from the conversation.',\n 'Do not use tools, commands, or file writes unless the question explicitly requires a tiny code snippet.',\n 'If the question can be answered briefly, answer briefly.',\n ].join('\\n');\n\n const userPrompt = [\n systemBlock,\n '',\n '---',\n 'Conversation background (read-only):',\n background || '(empty)',\n '',\n '---',\n 'Side question:',\n q,\n ].join('\\n');\n\n const userMessage: UserMessage = { role: 'user', content: userPrompt, timestamp: Date.now() };\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 90_000);\n try {\n const requestedMaxTokens = Math.max(1, Math.trunc(opts.maxTokens ?? 2048));\n const modelMaxTokens = typeof model.maxTokens === 'number' && Number.isFinite(model.maxTokens)\n ? Math.max(1, Math.trunc(model.maxTokens))\n : undefined;\n const out = await complete(model, { messages: [userMessage] }, {\n maxTokens: modelMaxTokens ? Math.min(requestedMaxTokens, modelMaxTokens) : requestedMaxTokens,\n temperature: opts.temperature ?? 0.4,\n signal: controller.signal as AbortSignal,\n });\n const text = textFromCompleteContent((out as { content?: unknown }).content).trim();\n return text ? { text } : { text: '', error: 'No text returned from model.' };\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n opts.log.warn({ err, sessionKey: opts.sessionKey, errorMessage: em }, 'btwQuery failed');\n return { text: '', error: em };\n } finally {\n clearTimeout(timeoutId);\n }\n}\n"],"mappings":";;;;gBAKwD;AAIxD,SAAS,wBAAwB,SAA0B;AACzD,KAAI,OAAO,YAAY,SACrB,QAAO;AAET,KAAI,CAAC,MAAM,QAAQ,QAAQ,CACzB,QAAO;AAET,QAAO,QACJ,QACE,MACC,GAAG,SAAS,UAAU,OAAQ,EAAyB,SAAS,SACnE,CACA,KAAK,MAAM,EAAE,QAAQ,GAAG,CACxB,KAAK,GAAG;;AAGb,SAAS,qBAAqB,UAAkC;AAC9D,QAAO,SACJ,KAAK,MAAM;EACV,MAAM,OAAO,EAAE;EACf,IAAI,OAAO;EACX,MAAM,MAAM,wBAAwB,EAAE;AACtC,MAAI,OAAO,QAAQ,SACjB,QAAO;WACE,MAAM,QAAQ,IAAI,CAC3B,QAAO,IACJ,QAAQ,MAA2C,EAAE,SAAS,OAAO,CACrE,KAAK,MAAM,EAAE,QAAQ,GAAG,CACxB,KAAK,KAAK;EAEf,MAAM,OAAO,IAAI,KAAK,KAAK;AAC3B,SAAO,KAAK,SAAS,MAAO,GAAG,KAAK,MAAM,GAAG,IAAK,CAAC,KAAK;GACxD,CACD,KAAK,OAAO;;;AAIjB,eAAsB,YAAY,MAQY;CAC5C,MAAM,IAAI,KAAK,SAAS,MAAM;AAC9B,KAAI,CAAC,EACH,QAAO;EAAE,MAAM;EAAI,OAAO;EAAmB;CAE/C,MAAM,WAAW,MAAM,KAAK,aAAa,KAAK,KAAK,WAAW;CAC9D,MAAM,WAAW,KAAK;CACtB,IAAI;AACJ,KAAI;AACF,UAAQ,aAAa,SAAS;UACvB,KAAK;EACZ,MAAM,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC3D,OAAK,IAAI,KAAK;GAAE;GAAK;GAAU,cAAc;GAAI,EAAE,iCAAiC;AACpF,SAAO;GAAE,MAAM;GAAI,OAAO,4BAA4B;GAAY;;CAGpE,MAAM,aAAa,qBAAqB,SAAS,MAAM,IAAI,CAAC;CAqB5D,MAAM,cAA2B;EAAE,MAAM;EAAQ,SAZ9B;GARC;IAClB;IACA;IACA;IACA;IACA;IACD,CAAC,KAAK,KAGM;GACX;GACA;GACA;GACA,cAAc;GACd;GACA;GACA;GACA;GACD,CAAC,KAAK,KAE6D;EAAE,WAAW,KAAK,KAAK;EAAE;CAC7F,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,YAAY,iBAAiB,WAAW,OAAO,EAAE,IAAO;AAC9D,KAAI;EACF,MAAM,qBAAqB,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,aAAa,KAAK,CAAC;EAC1E,MAAM,iBAAiB,OAAO,MAAM,cAAc,YAAY,OAAO,SAAS,MAAM,UAAU,GAC1F,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,UAAU,CAAC,GACxC,KAAA;EAMJ,MAAM,OAAO,yBAAyB,MALpB,SAAS,OAAO,EAAE,UAAU,CAAC,YAAY,EAAE,EAAE;GAC7D,WAAW,iBAAiB,KAAK,IAAI,oBAAoB,eAAe,GAAG;GAC3E,aAAa,KAAK,eAAe;GACjC,QAAQ,WAAW;GACpB,CAAC,EACkE,QAAQ,CAAC,MAAM;AACnF,SAAO,OAAO,EAAE,MAAM,GAAG;GAAE,MAAM;GAAI,OAAO;GAAgC;UACrE,KAAK;EACZ,MAAM,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC3D,OAAK,IAAI,KAAK;GAAE;GAAK,YAAY,KAAK;GAAY,cAAc;GAAI,EAAE,kBAAkB;AACxF,SAAO;GAAE,MAAM;GAAI,OAAO;GAAI;WACtB;AACR,eAAa,UAAU"}
1
+ {"version":3,"file":"btw-query.js","names":[],"sources":["../../../../src/agent/service/btw-query.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport { type UserMessage } from '@earendil-works/pi-ai/compat';\n\nimport { readAgentMessageContent } from '../memory/agent-message-access.js';\nimport type { SessionStore } from '../../session/index.js';\nimport type { CredentialResolverOptions } from '../../auth/credentials.js';\nimport { resolveModel } from '../../providers/index.js';\nimport {\n completeWithResolvedCredentials,\n createResolvedModelStream,\n resolveModelCallApiKey,\n} from '../../providers/model-call.js';\n\ntype BtwLog = { warn: (obj: Record<string, unknown>, msg: string) => void };\n\nfunction textFromCompleteContent(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n if (!Array.isArray(content)) {\n return '';\n }\n return content\n .filter(\n (c): c is { type: 'text'; text: string } =>\n c?.type === 'text' && typeof (c as { text?: unknown }).text === 'string',\n )\n .map((c) => c.text || '')\n .join('');\n}\n\nfunction formatMessagesForBtw(messages: AgentMessage[]): string {\n return messages\n .map((m) => {\n const role = m.role;\n let body = '';\n const raw = readAgentMessageContent(m);\n if (typeof raw === 'string') {\n body = raw;\n } else if (Array.isArray(raw)) {\n body = raw\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text')\n .map((c) => c.text || '')\n .join('\\n');\n }\n const line = `[${role}]: ${body}`;\n return line.length > 4000 ? `${line.slice(0, 4000)}…` : line;\n })\n .join('\\n\\n');\n}\n\n/** One-shot LLM answer for /btw: transcript as background only; does not persist. */\nexport async function runBtwQuery(opts: {\n sessionKey: string;\n question: string;\n sessionStore: SessionStore;\n modelForSession: string;\n log: BtwLog;\n maxTokens?: number;\n temperature?: number;\n includeSessionContext?: boolean;\n onTextDelta?: (delta: string) => void | Promise<void>;\n credentialOptions?: CredentialResolverOptions;\n}): Promise<{ text: string; error?: string }> {\n const q = opts.question.trim();\n if (!q) {\n return { text: '', error: 'Empty question.' };\n }\n const modelRef = opts.modelForSession;\n let model;\n try {\n model = resolveModel(modelRef);\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n opts.log.warn({ err, modelRef, errorMessage: em }, 'btwQuery: model resolve failed');\n return { text: '', error: `Could not resolve model: ${modelRef}` };\n }\n\n let apiKey: string | undefined;\n try {\n apiKey = await resolveModelCallApiKey(model, opts.credentialOptions);\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n opts.log.warn({ err, modelRef, provider: model.provider, errorMessage: em }, 'btwQuery: credential lookup failed');\n return { text: '', error: `Could not load credentials for provider: ${model.provider}` };\n }\n if (!apiKey) {\n opts.log.warn({ modelRef, provider: model.provider }, 'btwQuery: provider credentials are not configured');\n return { text: '', error: `No API key for provider: ${model.provider}` };\n }\n\n const includeSessionContext = opts.includeSessionContext ?? true;\n let userPrompt = q;\n if (includeSessionContext) {\n const messages = await opts.sessionStore.load(opts.sessionKey);\n const background = formatMessagesForBtw(messages.slice(-40));\n const systemBlock = [\n 'You are answering an ephemeral /btw side question about the current conversation.',\n 'Use the conversation only as background context.',\n 'Answer only the side question. Do not continue or complete any unfinished task from the conversation.',\n 'Do not use tools, commands, or file writes unless the question explicitly requires a tiny code snippet.',\n 'If the question can be answered briefly, answer briefly.',\n ].join('\\n');\n userPrompt = [\n systemBlock,\n '',\n '---',\n 'Conversation background (read-only):',\n background || '(empty)',\n '',\n '---',\n 'Side question:',\n q,\n ].join('\\n');\n }\n\n const userMessage: UserMessage = { role: 'user', content: userPrompt, timestamp: Date.now() };\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 90_000);\n try {\n const requestedMaxTokens = Math.max(1, Math.trunc(opts.maxTokens ?? 2048));\n const modelMaxTokens = typeof model.maxTokens === 'number' && Number.isFinite(model.maxTokens)\n ? Math.max(1, Math.trunc(model.maxTokens))\n : undefined;\n const modelCallOptions = {\n maxTokens: modelMaxTokens ? Math.min(requestedMaxTokens, modelMaxTokens) : requestedMaxTokens,\n signal: controller.signal as AbortSignal,\n apiKey,\n ...(opts.temperature !== undefined ? { temperature: opts.temperature } : {}),\n };\n let streamedText = '';\n const out = opts.onTextDelta\n ? await (async () => {\n const stream = await createResolvedModelStream(\n model,\n { messages: [userMessage] },\n modelCallOptions,\n opts.credentialOptions,\n );\n for await (const event of stream) {\n const streamEvent = event as { type?: unknown; delta?: unknown; error?: unknown };\n if (streamEvent.type === 'text_delta' && typeof streamEvent.delta === 'string') {\n streamedText += streamEvent.delta;\n await opts.onTextDelta?.(streamEvent.delta);\n continue;\n }\n if (streamEvent.type === 'error') {\n const error = streamEvent.error;\n const message = error && typeof error === 'object' && 'errorMessage' in error\n ? (error as { errorMessage?: unknown }).errorMessage\n : undefined;\n throw new Error(typeof message === 'string' && message ? message : 'Model stream failed');\n }\n }\n return await stream.result();\n })()\n : await completeWithResolvedCredentials(model, { messages: [userMessage] }, modelCallOptions, opts.credentialOptions);\n const response = out as {\n content?: unknown;\n stopReason?: unknown;\n errorMessage?: unknown;\n usage?: { output?: unknown; reasoning?: unknown };\n };\n const text = (streamedText || textFromCompleteContent(response.content)).trim();\n if (text) return { text };\n\n const stopReason = typeof response.stopReason === 'string' ? response.stopReason : undefined;\n const errorMessage = typeof response.errorMessage === 'string' ? response.errorMessage.trim() : '';\n if (stopReason === 'error' && errorMessage) {\n opts.log.warn({ sessionKey: opts.sessionKey, modelRef, stopReason, errorMessage }, 'btwQuery model call failed');\n return { text: '', error: errorMessage };\n }\n const contentTypes = Array.isArray(response.content)\n ? response.content\n .map((block) => block && typeof block === 'object' && 'type' in block\n ? String((block as { type?: unknown }).type)\n : 'unknown')\n .join(', ')\n : undefined;\n const reasoningTokens = typeof response.usage?.reasoning === 'number' ? response.usage.reasoning : undefined;\n const detail = [\n stopReason ? `stop reason: ${stopReason}` : '',\n contentTypes ? `content: ${contentTypes}` : '',\n reasoningTokens != null ? `reasoning tokens: ${reasoningTokens}` : '',\n ].filter(Boolean).join('; ');\n const error = `No text returned from model${detail ? ` (${detail})` : ''}.`;\n opts.log.warn({\n sessionKey: opts.sessionKey,\n modelRef,\n stopReason,\n errorMessage: errorMessage || undefined,\n contentTypes,\n reasoningTokens,\n }, 'btwQuery returned no text');\n return { text: '', error };\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n opts.log.warn({ err, sessionKey: opts.sessionKey, errorMessage: em }, 'btwQuery failed');\n return { text: '', error: em };\n } finally {\n clearTimeout(timeoutId);\n }\n}\n"],"mappings":"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"}
@@ -48,6 +48,8 @@ var SessionInspector = class {
48
48
  }
49
49
  /** One-shot LLM answer for `/btw`: transcript as background, not persisted. */
50
50
  btwQuery(sessionKey, question, options) {
51
+ const config = this.opts.getConfig();
52
+ const profile = config ? resolveEffectiveAgentProfileForSession(config, sessionKey) : void 0;
51
53
  return runBtwQuery({
52
54
  sessionKey,
53
55
  question,
@@ -55,7 +57,13 @@ var SessionInspector = class {
55
57
  modelForSession: options?.modelRef?.trim() || this.opts.modelManager.getModelForSession(sessionKey),
56
58
  log,
57
59
  maxTokens: options?.maxTokens,
58
- temperature: options?.temperature
60
+ temperature: options?.temperature,
61
+ includeSessionContext: options?.includeSessionContext,
62
+ onTextDelta: options?.onTextDelta,
63
+ credentialOptions: profile && config ? {
64
+ agentId: profile.agentId,
65
+ appConfig: config
66
+ } : void 0
59
67
  });
60
68
  }
61
69
  /** Cheap stats used by both the report and other callers. */
@@ -1 +1 @@
1
- {"version":3,"file":"session-inspector.js","names":[],"sources":["../../../../src/agent/session/session-inspector.ts"],"sourcesContent":["/**\n * SessionInspector — read-only / introspection-oriented operations on a session.\n *\n * Owns the five methods previously scattered across `AgentService` that all\n * compute a \"summary view\" of one session:\n * - `compact` (manual user-triggered compaction)\n * - `btwQuery` (one-shot LLM answer with transcript as background)\n * - `report` (Markdown / JSON `/context` summary)\n * - `agentConfig` (resolved thinking + model + workspace for the Web UI)\n * - `contextUsage` (rough token budget vs estimated transcript)\n *\n * The shared helper `computeStats` keeps the three callsites that need\n * `getWindowStats` / `getCompactionStats` / `estimateTokenUsage` in lockstep.\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { ThinkingLevel } from '@earendil-works/pi-agent-core';\n\nimport type { Config } from '../../config/schema.js';\nimport type { BtwQueryOptions } from '../../chat-commands/index.js';\nimport { resolveEffectiveAgentProfileForSession } from '../../config/agent-profile.js';\nimport { resolveDefaultAgentWorkspaceDir } from '../../config/workspace-defaults.js';\nimport {\n effectiveWorkspacePathForSession,\n projectWorkspacePath,\n resolveEffectiveReasoningLevel,\n resolveEffectiveThinkingLevel,\n resolveVerboseLevel,\n type SessionConfigStore,\n} from '../../session/index.js';\nimport { getProjectForSession } from '../../projects/workspace.js';\nimport type { SessionStore } from '../../session/store.js';\nimport type { CompactionResult } from '../memory/compaction.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { AgentInstanceGateway } from '../agent-instance-gateway.js';\nimport { runBtwQuery } from '../service/btw-query.js';\nimport { formatSessionContextReport } from '../service/session-context-report.js';\nimport type { ReasoningLevel, VerboseLevel } from '../transcript/thinking-types.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { SessionHydrator } from './session-hydrator.js';\n\nconst log = createLogger('SessionInspector');\n\nexport interface SessionInspectorOptions {\n sessionStore: SessionStore;\n sessionConfigStore: SessionConfigStore;\n modelManager: ModelManager;\n agentManager: AgentInstanceGateway;\n sessionHydrator: SessionHydrator;\n /** Effective config snapshot accessor (honours runtime overrides). */\n getConfig: () => Config | undefined;\n /**\n * Nominal context window the session is budgeted against. Derived from the\n * effective session model metadata, defaulting to 128k.\n */\n getContextWindow: (sessionKey: string) => number;\n}\n\nexport interface SessionContextUsage {\n estimatedTokens: number;\n contextWindow: number;\n usagePercent: number | null;\n}\n\nexport interface SessionAgentConfigView {\n thinkingLevel: ThinkingLevel;\n model: string;\n reasoningLevel: ReasoningLevel;\n verboseLevel: VerboseLevel;\n effectiveWorkspacePath: string;\n workingDirectoryLocked: boolean;\n workspaceSource: 'project' | 'session_override' | 'agent_default_root' | 'agent_workspace';\n}\n\nexport class SessionInspector {\n private readonly opts: SessionInspectorOptions;\n\n constructor(opts: SessionInspectorOptions) {\n this.opts = opts;\n }\n\n private async ensureEffectiveSessionModel(sessionKey: string): Promise<void> {\n await this.opts.sessionHydrator.model(sessionKey);\n const cfg = this.opts.getConfig();\n if (!cfg) return;\n\n const profile = resolveEffectiveAgentProfileForSession(cfg, sessionKey);\n const profileModelRef = profile.primaryModelRef?.trim();\n if (profileModelRef) {\n this.opts.modelManager.setSessionProfileDefault(sessionKey, profileModelRef, profile.fallbacks);\n }\n }\n\n private async contextWindowForSession(sessionKey: string): Promise<number> {\n await this.ensureEffectiveSessionModel(sessionKey);\n return this.opts.getContextWindow(sessionKey);\n }\n\n /**\n * Manual compaction triggered by a user / API. Always forces a compaction\n * pass (`force: true` by default) and evicts the in-memory agent so the\n * next turn reloads from the compacted transcript.\n */\n async compact(\n sessionKey: string,\n options?: { instructions?: string; force?: boolean },\n ): Promise<CompactionResult> {\n const messages = await this.opts.sessionStore.load(sessionKey);\n const contextWindow = await this.contextWindowForSession(sessionKey);\n const result = await this.opts.sessionStore.compact(\n sessionKey,\n messages,\n contextWindow,\n options?.instructions,\n options?.force ?? true,\n );\n if (result.compacted) {\n this.opts.agentManager.removeAgent(sessionKey);\n }\n log.info({ sessionKey, result }, 'Manual compaction complete');\n return result;\n }\n\n /** One-shot LLM answer for `/btw`: transcript as background, not persisted. */\n btwQuery(\n sessionKey: string,\n question: string,\n options?: BtwQueryOptions,\n ): Promise<{ text: string; error?: string }> {\n return runBtwQuery({\n sessionKey,\n question,\n sessionStore: this.opts.sessionStore,\n modelForSession: options?.modelRef?.trim() || this.opts.modelManager.getModelForSession(sessionKey),\n log,\n maxTokens: options?.maxTokens,\n temperature: options?.temperature,\n });\n }\n\n /** Cheap stats used by both the report and other callers. */\n stats(sessionKey: string, messages: AgentMessage[]): {\n windowStats: ReturnType<SessionStore['getWindowStats']>;\n compactionStats: ReturnType<SessionStore['getCompactionStats']>;\n tokenEstimate: ReturnType<SessionStore['estimateTokenUsage']>;\n } {\n return {\n windowStats: this.opts.sessionStore.getWindowStats(messages),\n compactionStats: this.opts.sessionStore.getCompactionStats(sessionKey),\n tokenEstimate: this.opts.sessionStore.estimateTokenUsage(sessionKey, messages),\n };\n }\n\n /** Rough context usage for TUI footer (estimated tokens vs nominal budget). */\n async contextUsage(sessionKey: string): Promise<SessionContextUsage> {\n const messages = await this.opts.sessionStore.load(sessionKey);\n const contextWindow = await this.contextWindowForSession(sessionKey);\n const estimatedTokens = await this.opts.sessionStore.estimateTokenUsage(sessionKey, messages);\n const usagePercent =\n contextWindow > 0 ? Math.min(100, Math.round((estimatedTokens / contextWindow) * 100)) : null;\n return { estimatedTokens, contextWindow, usagePercent };\n }\n\n /** Markdown or JSON summary for `/context`. */\n async report(sessionKey: string, mode: 'list' | 'detail' | 'json'): Promise<string> {\n const cfg = this.opts.getConfig();\n if (!cfg) {\n throw new Error('SessionInspector requires a config snapshot to render report');\n }\n const messages = await this.opts.sessionStore.load(sessionKey);\n const cw = await this.contextWindowForSession(sessionKey);\n const computed = this.stats(sessionKey, messages);\n const model = this.opts.modelManager.getModelForSession(sessionKey);\n const sc = await this.opts.sessionConfigStore.get(sessionKey);\n const project = getProjectForSession(sessionKey);\n const workspace = effectiveWorkspacePathForSession(cfg, sessionKey, sc, project);\n const estTokens = await this.opts.sessionStore.estimateTokenUsage(sessionKey, messages);\n const profile = resolveEffectiveAgentProfileForSession(cfg, sessionKey);\n const deniedTools = [...profile.tools.denied].sort((a, b) => a.localeCompare(b));\n const toolsSummary = deniedTools.length > 0 ? `denied: ${deniedTools.join(', ')}` : '(no denied tools)';\n\n return formatSessionContextReport({\n sessionKey,\n mode,\n model,\n workspacePath: workspace,\n agentId: profile.agentId,\n messageCount: messages.length,\n contextWindowNominal: cw,\n estimatedTranscriptTokens: estTokens,\n thinkingDefault: undefined,\n reasoningDefault: undefined,\n verboseDefault: undefined,\n compaction: undefined,\n toolsFlagsSummary: toolsSummary,\n windowStats: computed.windowStats,\n compactionRunStats: computed.compactionStats,\n });\n }\n\n /** Resolved thinking / model / workspace for the Web UI. */\n async agentConfig(sessionKey: string): Promise<SessionAgentConfigView> {\n await this.ensureEffectiveSessionModel(sessionKey);\n const cfg = this.opts.getConfig();\n if (!cfg) {\n throw new Error('SessionInspector requires a config snapshot to resolve agent config');\n }\n const sc = await this.opts.sessionConfigStore.get(sessionKey);\n\n const defThink = 'medium';\n const level = await resolveEffectiveThinkingLevel(this.opts.sessionConfigStore, sessionKey, null, defThink);\n const defReason = 'stream' as ReasoningLevel;\n const reasoningLevel = await resolveEffectiveReasoningLevel(this.opts.sessionConfigStore, sessionKey, defReason);\n const defVerbose = 'full' as VerboseLevel;\n const verboseLevel = await resolveVerboseLevel(this.opts.sessionConfigStore, sessionKey, defVerbose);\n const model = this.opts.modelManager.getModelForSession(sessionKey);\n const project = getProjectForSession(sessionKey);\n const projectWorkspace = projectWorkspacePath(project);\n const hasSessionWorkspaceOverride = Boolean(sc?.workingDirectoryOverride?.trim());\n const effectiveWorkspacePath = effectiveWorkspacePathForSession(cfg, sessionKey, sc, project);\n const isDefaultWorkspaceRoot = effectiveWorkspacePath === resolveDefaultAgentWorkspaceDir();\n return {\n thinkingLevel: level,\n model,\n reasoningLevel,\n verboseLevel,\n effectiveWorkspacePath,\n workingDirectoryLocked: Boolean(projectWorkspace || hasSessionWorkspaceOverride),\n workspaceSource: projectWorkspace\n ? 'project'\n : hasSessionWorkspaceOverride\n ? 'session_override'\n : isDefaultWorkspaceRoot\n ? 'agent_default_root'\n : 'agent_workspace',\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"session-inspector.js","names":[],"sources":["../../../../src/agent/session/session-inspector.ts"],"sourcesContent":["/**\n * SessionInspector — read-only / introspection-oriented operations on a session.\n *\n * Owns the five methods previously scattered across `AgentService` that all\n * compute a \"summary view\" of one session:\n * - `compact` (manual user-triggered compaction)\n * - `btwQuery` (one-shot LLM answer with transcript as background)\n * - `report` (Markdown / JSON `/context` summary)\n * - `agentConfig` (resolved thinking + model + workspace for the Web UI)\n * - `contextUsage` (rough token budget vs estimated transcript)\n *\n * The shared helper `computeStats` keeps the three callsites that need\n * `getWindowStats` / `getCompactionStats` / `estimateTokenUsage` in lockstep.\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { ThinkingLevel } from '@earendil-works/pi-agent-core';\n\nimport type { Config } from '../../config/schema.js';\nimport type { BtwQueryOptions } from '../../chat-commands/index.js';\nimport { resolveEffectiveAgentProfileForSession } from '../../config/agent-profile.js';\nimport { resolveDefaultAgentWorkspaceDir } from '../../config/workspace-defaults.js';\nimport {\n effectiveWorkspacePathForSession,\n projectWorkspacePath,\n resolveEffectiveReasoningLevel,\n resolveEffectiveThinkingLevel,\n resolveVerboseLevel,\n type SessionConfigStore,\n} from '../../session/index.js';\nimport { getProjectForSession } from '../../projects/workspace.js';\nimport type { SessionStore } from '../../session/store.js';\nimport type { CompactionResult } from '../memory/compaction.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { AgentInstanceGateway } from '../agent-instance-gateway.js';\nimport { runBtwQuery } from '../service/btw-query.js';\nimport { formatSessionContextReport } from '../service/session-context-report.js';\nimport type { ReasoningLevel, VerboseLevel } from '../transcript/thinking-types.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { SessionHydrator } from './session-hydrator.js';\n\nconst log = createLogger('SessionInspector');\n\nexport interface SessionInspectorOptions {\n sessionStore: SessionStore;\n sessionConfigStore: SessionConfigStore;\n modelManager: ModelManager;\n agentManager: AgentInstanceGateway;\n sessionHydrator: SessionHydrator;\n /** Effective config snapshot accessor (honours runtime overrides). */\n getConfig: () => Config | undefined;\n /**\n * Nominal context window the session is budgeted against. Derived from the\n * effective session model metadata, defaulting to 128k.\n */\n getContextWindow: (sessionKey: string) => number;\n}\n\nexport interface SessionContextUsage {\n estimatedTokens: number;\n contextWindow: number;\n usagePercent: number | null;\n}\n\nexport interface SessionAgentConfigView {\n thinkingLevel: ThinkingLevel;\n model: string;\n reasoningLevel: ReasoningLevel;\n verboseLevel: VerboseLevel;\n effectiveWorkspacePath: string;\n workingDirectoryLocked: boolean;\n workspaceSource: 'project' | 'session_override' | 'agent_default_root' | 'agent_workspace';\n}\n\nexport class SessionInspector {\n private readonly opts: SessionInspectorOptions;\n\n constructor(opts: SessionInspectorOptions) {\n this.opts = opts;\n }\n\n private async ensureEffectiveSessionModel(sessionKey: string): Promise<void> {\n await this.opts.sessionHydrator.model(sessionKey);\n const cfg = this.opts.getConfig();\n if (!cfg) return;\n\n const profile = resolveEffectiveAgentProfileForSession(cfg, sessionKey);\n const profileModelRef = profile.primaryModelRef?.trim();\n if (profileModelRef) {\n this.opts.modelManager.setSessionProfileDefault(sessionKey, profileModelRef, profile.fallbacks);\n }\n }\n\n private async contextWindowForSession(sessionKey: string): Promise<number> {\n await this.ensureEffectiveSessionModel(sessionKey);\n return this.opts.getContextWindow(sessionKey);\n }\n\n /**\n * Manual compaction triggered by a user / API. Always forces a compaction\n * pass (`force: true` by default) and evicts the in-memory agent so the\n * next turn reloads from the compacted transcript.\n */\n async compact(\n sessionKey: string,\n options?: { instructions?: string; force?: boolean },\n ): Promise<CompactionResult> {\n const messages = await this.opts.sessionStore.load(sessionKey);\n const contextWindow = await this.contextWindowForSession(sessionKey);\n const result = await this.opts.sessionStore.compact(\n sessionKey,\n messages,\n contextWindow,\n options?.instructions,\n options?.force ?? true,\n );\n if (result.compacted) {\n this.opts.agentManager.removeAgent(sessionKey);\n }\n log.info({ sessionKey, result }, 'Manual compaction complete');\n return result;\n }\n\n /** One-shot LLM answer for `/btw`: transcript as background, not persisted. */\n btwQuery(\n sessionKey: string,\n question: string,\n options?: BtwQueryOptions,\n ): Promise<{ text: string; error?: string }> {\n const config = this.opts.getConfig();\n const profile = config ? resolveEffectiveAgentProfileForSession(config, sessionKey) : undefined;\n return runBtwQuery({\n sessionKey,\n question,\n sessionStore: this.opts.sessionStore,\n modelForSession: options?.modelRef?.trim() || this.opts.modelManager.getModelForSession(sessionKey),\n log,\n maxTokens: options?.maxTokens,\n temperature: options?.temperature,\n includeSessionContext: options?.includeSessionContext,\n onTextDelta: options?.onTextDelta,\n credentialOptions: profile && config\n ? { agentId: profile.agentId, appConfig: config }\n : undefined,\n });\n }\n\n /** Cheap stats used by both the report and other callers. */\n stats(sessionKey: string, messages: AgentMessage[]): {\n windowStats: ReturnType<SessionStore['getWindowStats']>;\n compactionStats: ReturnType<SessionStore['getCompactionStats']>;\n tokenEstimate: ReturnType<SessionStore['estimateTokenUsage']>;\n } {\n return {\n windowStats: this.opts.sessionStore.getWindowStats(messages),\n compactionStats: this.opts.sessionStore.getCompactionStats(sessionKey),\n tokenEstimate: this.opts.sessionStore.estimateTokenUsage(sessionKey, messages),\n };\n }\n\n /** Rough context usage for TUI footer (estimated tokens vs nominal budget). */\n async contextUsage(sessionKey: string): Promise<SessionContextUsage> {\n const messages = await this.opts.sessionStore.load(sessionKey);\n const contextWindow = await this.contextWindowForSession(sessionKey);\n const estimatedTokens = await this.opts.sessionStore.estimateTokenUsage(sessionKey, messages);\n const usagePercent =\n contextWindow > 0 ? Math.min(100, Math.round((estimatedTokens / contextWindow) * 100)) : null;\n return { estimatedTokens, contextWindow, usagePercent };\n }\n\n /** Markdown or JSON summary for `/context`. */\n async report(sessionKey: string, mode: 'list' | 'detail' | 'json'): Promise<string> {\n const cfg = this.opts.getConfig();\n if (!cfg) {\n throw new Error('SessionInspector requires a config snapshot to render report');\n }\n const messages = await this.opts.sessionStore.load(sessionKey);\n const cw = await this.contextWindowForSession(sessionKey);\n const computed = this.stats(sessionKey, messages);\n const model = this.opts.modelManager.getModelForSession(sessionKey);\n const sc = await this.opts.sessionConfigStore.get(sessionKey);\n const project = getProjectForSession(sessionKey);\n const workspace = effectiveWorkspacePathForSession(cfg, sessionKey, sc, project);\n const estTokens = await this.opts.sessionStore.estimateTokenUsage(sessionKey, messages);\n const profile = resolveEffectiveAgentProfileForSession(cfg, sessionKey);\n const deniedTools = [...profile.tools.denied].sort((a, b) => a.localeCompare(b));\n const toolsSummary = deniedTools.length > 0 ? `denied: ${deniedTools.join(', ')}` : '(no denied tools)';\n\n return formatSessionContextReport({\n sessionKey,\n mode,\n model,\n workspacePath: workspace,\n agentId: profile.agentId,\n messageCount: messages.length,\n contextWindowNominal: cw,\n estimatedTranscriptTokens: estTokens,\n thinkingDefault: undefined,\n reasoningDefault: undefined,\n verboseDefault: undefined,\n compaction: undefined,\n toolsFlagsSummary: toolsSummary,\n windowStats: computed.windowStats,\n compactionRunStats: computed.compactionStats,\n });\n }\n\n /** Resolved thinking / model / workspace for the Web UI. */\n async agentConfig(sessionKey: string): Promise<SessionAgentConfigView> {\n await this.ensureEffectiveSessionModel(sessionKey);\n const cfg = this.opts.getConfig();\n if (!cfg) {\n throw new Error('SessionInspector requires a config snapshot to resolve agent config');\n }\n const sc = await this.opts.sessionConfigStore.get(sessionKey);\n\n const defThink = 'medium';\n const level = await resolveEffectiveThinkingLevel(this.opts.sessionConfigStore, sessionKey, null, defThink);\n const defReason = 'stream' as ReasoningLevel;\n const reasoningLevel = await resolveEffectiveReasoningLevel(this.opts.sessionConfigStore, sessionKey, defReason);\n const defVerbose = 'full' as VerboseLevel;\n const verboseLevel = await resolveVerboseLevel(this.opts.sessionConfigStore, sessionKey, defVerbose);\n const model = this.opts.modelManager.getModelForSession(sessionKey);\n const project = getProjectForSession(sessionKey);\n const projectWorkspace = projectWorkspacePath(project);\n const hasSessionWorkspaceOverride = Boolean(sc?.workingDirectoryOverride?.trim());\n const effectiveWorkspacePath = effectiveWorkspacePathForSession(cfg, sessionKey, sc, project);\n const isDefaultWorkspaceRoot = effectiveWorkspacePath === resolveDefaultAgentWorkspaceDir();\n return {\n thinkingLevel: level,\n model,\n reasoningLevel,\n verboseLevel,\n effectiveWorkspacePath,\n workingDirectoryLocked: Boolean(projectWorkspace || hasSessionWorkspaceOverride),\n workspaceSource: projectWorkspace\n ? 'project'\n : hasSessionWorkspaceOverride\n ? 'session_override'\n : isDefaultWorkspaceRoot\n ? 'agent_default_root'\n : 'agent_workspace',\n };\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
3
  import { SHORT_TERM_PROMOTION_LOCK_RELATIVE, SHORT_TERM_RECALL_STORE_RELATIVE } from "../memory/dreaming/constants.js";
4
- import { loadDreamingStore, saveDreamingStore } from "../memory/dreaming/short-term-store.js";
4
+ import { clearStaleDreamingLock, loadDreamingStore, resetDreamingStore } from "../memory/dreaming/short-term-store.js";
5
5
  import { resolveDreamingConfig } from "../memory/dreaming/config.js";
6
6
  import path from "node:path";
7
7
  import fs from "node:fs/promises";
@@ -79,23 +79,14 @@ function createDreamingTool(deps) {
79
79
  ].join("\n"));
80
80
  }
81
81
  if (action === "reset_store") {
82
- const { store } = await loadDreamingStore({ dreamingRoot });
83
- const before = Object.keys(store.entries ?? {}).length;
84
- const nowIso = (/* @__PURE__ */ new Date()).toISOString();
85
- store.entries = {};
86
- store.updatedAt = nowIso;
87
- await saveDreamingStore({
88
- dreamingRoot,
89
- store
90
- });
82
+ const before = await resetDreamingStore({ dreamingRoot });
91
83
  log.info({
92
84
  dreamingRoot,
93
85
  before
94
86
  }, "Dreaming store reset");
95
87
  return textResult(`Reset short-term store. Removed ${before} entr${before === 1 ? "y" : "ies"}. Path: ${storePath}`);
96
88
  }
97
- await fs.unlink(lockPath).catch(() => {});
98
- return textResult(`Cleared lock (if present). Path: ${lockPath}`);
89
+ return textResult(`${await clearStaleDreamingLock(dreamingRoot) ? "Cleared stale lock." : "No lock present."} Path: ${lockPath}`);
99
90
  }
100
91
  };
101
92
  }