@xopcai/xopc 0.0.117 → 0.0.118

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 (277) hide show
  1. package/README.md +24 -23
  2. package/README.zh-CN.md +45 -42
  3. package/dist/browser-ext/manifest.json +1 -1
  4. package/dist/extensions/telegram/xopc.extension.json +1 -1
  5. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-BBLhJ2C6.js → agent-avatar-dicebear-dzUrQoeZ.js} +1 -1
  6. package/dist/gateway/static/root/assets/agents-BCxZbkOw.js +2 -0
  7. package/dist/gateway/static/root/assets/{app-management-settings-panel-CBpLONvG.js → app-management-settings-panel-bxYJVJpx.js} +1 -1
  8. package/dist/gateway/static/root/assets/{appearance-settings-CGcc54yj.js → appearance-settings-BCIykXlI.js} +1 -1
  9. package/dist/gateway/static/root/assets/{apps-page-DdDx0V4z.js → apps-page--wMEL00f.js} +1 -1
  10. package/dist/gateway/static/root/assets/{archive-plugin-DZsVqMhU.js → archive-plugin-Cki4SfU2.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-CIol6JWX.js → attachment-load-D-KnS2Do.js} +1 -1
  12. package/dist/gateway/static/root/assets/{binary-plugins-B3UauMfp.js → binary-plugins-D2sgvJja.js} +1 -1
  13. package/dist/gateway/static/root/assets/{block-editor-2HJoFchH.js → block-editor-DIq-4Gvb.js} +27 -27
  14. package/dist/gateway/static/root/assets/browser-settings-page-DRhKdn81.js +8 -0
  15. package/dist/gateway/static/root/assets/{capability-presets-api-XQoMTI0A.js → capability-presets-api-CorhiHlg.js} +1 -1
  16. package/dist/gateway/static/root/assets/capability-presets-settings-panel-qFx9Jtce.js +1 -0
  17. package/dist/gateway/static/root/assets/channels-settings-BAwAkqIA.js +1 -0
  18. package/dist/gateway/static/root/assets/connectors-page-BQLcfitM.js +2 -0
  19. package/dist/gateway/static/root/assets/{cron-api-R41aQCX9.js → cron-api-qGwmL-24.js} +1 -1
  20. package/dist/gateway/static/root/assets/cron-page-BBWMAj8-.js +3 -0
  21. package/dist/gateway/static/root/assets/{dreaming-settings-Bceaw1PG.js → dreaming-settings-Dw89Obml.js} +1 -1
  22. package/dist/gateway/static/root/assets/{extension-debug-page-DX5WRE_t.js → extension-debug-page-DYu1KaFQ.js} +1 -1
  23. package/dist/gateway/static/root/assets/{extension-page-Buah7Bw-.js → extension-page-B3W6QwlZ.js} +1 -1
  24. package/dist/gateway/static/root/assets/{extension-settings-page-DuroLVnf.js → extension-settings-page-D7VQ-cxE.js} +1 -1
  25. package/dist/gateway/static/root/assets/{fetch-B5zdl27C.js → fetch-B7xDPu-R.js} +1 -1
  26. package/dist/gateway/static/root/assets/{format-interval-label-Dn_QKIqb.js → format-interval-label-BzxafKxR.js} +1 -1
  27. package/dist/gateway/static/root/assets/{gateway-config-api-gT4ZhE7h.js → gateway-config-api-BTBLSeUu.js} +1 -1
  28. package/dist/gateway/static/root/assets/{gateway-settings-iISWAZRm.js → gateway-settings-Cmwy58rS.js} +1 -1
  29. package/dist/gateway/static/root/assets/{gateway-startup-retry-B3kL8KGY.js → gateway-startup-retry-CYj9FWCp.js} +1 -1
  30. package/dist/gateway/static/root/assets/goal-detail-page-l7mu5GWX.js +1 -0
  31. package/dist/gateway/static/root/assets/{goals-config-api-AR-fVh0P.js → goals-config-api-CQQgzPr3.js} +1 -1
  32. package/dist/gateway/static/root/assets/{goals-page-Cpb21ezg.js → goals-page-BHitvJAv.js} +2 -2
  33. package/dist/gateway/static/root/assets/{goals-settings-CPAuFwoD.js → goals-settings-CYVY4NGp.js} +1 -1
  34. package/dist/gateway/static/root/assets/{heartbeat-config-api-D7bKFEeO.js → heartbeat-config-api-DriO_CN9.js} +1 -1
  35. package/dist/gateway/static/root/assets/heartbeat-settings-CUQTKxg7.js +1 -0
  36. package/dist/gateway/static/root/assets/index-C17Tsd_b.css +1 -0
  37. package/dist/gateway/static/root/assets/{index-CwhK8qCh.js → index-Cc41r16l.js} +120 -51
  38. package/dist/gateway/static/root/assets/{interval-presets-0DtsnxE0.js → interval-presets-DFPCKW_W.js} +1 -1
  39. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-BPREtN8B.js → keyboard-shortcuts-settings-CcVcqstx.js} +1 -1
  40. package/dist/gateway/static/root/assets/{logs-page-DefCgF4o.js → logs-page-C2Uqvae9.js} +2 -2
  41. package/dist/gateway/static/root/assets/{markdown-split-ChbR9LGO.js → markdown-split-BwOFrJ2E.js} +1 -1
  42. package/dist/gateway/static/root/assets/{media-plugins-B82oP80n.js → media-plugins-Cnz6Q5G6.js} +1 -1
  43. package/dist/gateway/static/root/assets/messages-B1iSKjiw.js +3 -0
  44. package/dist/gateway/static/root/assets/models-hub-panel-BfLfcta8.js +2 -0
  45. package/dist/gateway/static/root/assets/note-detail-page-BjwOQBfT.js +2 -0
  46. package/dist/gateway/static/root/assets/{note-image-lightbox-CDknITM3.js → note-image-lightbox-B-Svpmza.js} +1 -1
  47. package/dist/gateway/static/root/assets/notes-page-DktdBiwB.js +1 -0
  48. package/dist/gateway/static/root/assets/{preview-open-alternatives-DV3xQsWB.js → preview-open-alternatives-Bss4TiwP.js} +1 -1
  49. package/dist/gateway/static/root/assets/remote-access-hub-rfVx96fq.js +1 -0
  50. package/dist/gateway/static/root/assets/{save-bar-controls-BODCXnvY.js → save-bar-controls-BRNjdgKq.js} +1 -1
  51. package/dist/gateway/static/root/assets/{schedule-field-CgJSCD3B.js → schedule-field-CUCKODde.js} +1 -1
  52. package/dist/gateway/static/root/assets/{schema-form-qtfEI6QM.js → schema-form-Dub9sy8r.js} +1 -1
  53. package/dist/gateway/static/root/assets/sessions-page-DbtMFuuH.js +1 -0
  54. package/dist/gateway/static/root/assets/settings-advanced-gate-5re5gAnP.js +1 -0
  55. package/dist/gateway/static/root/assets/{settings-collapsible-section-BHZKjVcS.js → settings-collapsible-section-BeFLavGi.js} +1 -1
  56. package/dist/gateway/static/root/assets/{settings-form-section-5i35D-J1.js → settings-form-section-dmyaPOTG.js} +1 -1
  57. package/dist/gateway/static/root/assets/settings-page-D-WMyLlj.js +1 -0
  58. package/dist/gateway/static/root/assets/setup-status-panel-Cl-DEJAU.js +1 -0
  59. package/dist/gateway/static/root/assets/{share-preview-page-B0H63tDv.js → share-preview-page-C8glrdqe.js} +1 -1
  60. package/dist/gateway/static/root/assets/shares-settings-CyPVvbLt.js +1 -0
  61. package/dist/gateway/static/root/assets/skill-reload-api-CleTH1oz.js +1 -0
  62. package/dist/gateway/static/root/assets/skills-page-Ck8hdXqR.js +2 -0
  63. package/dist/gateway/static/root/assets/{system-settings-panel-DXs2VmBt.js → system-settings-panel-DI-LN_8G.js} +1 -1
  64. package/dist/gateway/static/root/assets/{typed-models-lib-4gPuydz_.js → typed-models-lib-kX9duch4.js} +1 -1
  65. package/dist/gateway/static/root/assets/{use-channel-catalog-BCjP_VHb.js → use-channel-catalog-DqlWTsDb.js} +1 -1
  66. package/dist/gateway/static/root/assets/user-profile-settings-panel-B-UFPMXy.js +1 -0
  67. package/dist/gateway/static/root/assets/utils-Dm0hKalR.js +1 -0
  68. package/dist/gateway/static/root/assets/voice-api-key-field-C73IVZeC.js +1 -0
  69. package/dist/gateway/static/root/assets/{voice-player-store-BlF9V05n.js → voice-player-store-C_ArLJPM.js} +1 -1
  70. package/dist/gateway/static/root/assets/workflows-page-BtFGp2y_.js +18 -0
  71. package/dist/gateway/static/root/index.html +5 -5
  72. package/dist/package.js +1 -1
  73. package/dist/src/agent/agent-manager.js +2 -1
  74. package/dist/src/agent/agent-manager.js.map +1 -1
  75. package/dist/src/agent/agent-scope.js +0 -2
  76. package/dist/src/agent/agent-scope.js.map +1 -1
  77. package/dist/src/agent/background-review/prompts.d.ts +2 -2
  78. package/dist/src/agent/background-review/prompts.js +2 -2
  79. package/dist/src/agent/background-review/prompts.js.map +1 -1
  80. package/dist/src/agent/background-review/settings.d.ts +1 -1
  81. package/dist/src/agent/background-review/settings.js +7 -8
  82. package/dist/src/agent/background-review/settings.js.map +1 -1
  83. package/dist/src/agent/bootstrap/bootstrap-cache.d.ts +1 -0
  84. package/dist/src/agent/bootstrap/bootstrap-cache.js +4 -3
  85. package/dist/src/agent/bootstrap/bootstrap-cache.js.map +1 -1
  86. package/dist/src/agent/bootstrap/bootstrap-files.d.ts +4 -0
  87. package/dist/src/agent/bootstrap/bootstrap-files.js +4 -3
  88. package/dist/src/agent/bootstrap/bootstrap-files.js.map +1 -1
  89. package/dist/src/agent/bootstrap/filter-bootstrap-files.js +4 -2
  90. package/dist/src/agent/bootstrap/filter-bootstrap-files.js.map +1 -1
  91. package/dist/src/agent/bootstrap/load-bootstrap-files.d.ts +2 -1
  92. package/dist/src/agent/bootstrap/load-bootstrap-files.js +17 -16
  93. package/dist/src/agent/bootstrap/load-bootstrap-files.js.map +1 -1
  94. package/dist/src/agent/bootstrap/types.d.ts +1 -1
  95. package/dist/src/agent/context/workspace-templates/AGENTS.md +1 -1
  96. package/dist/src/agent/context/workspace.d.ts +4 -2
  97. package/dist/src/agent/context/workspace.js +5 -4
  98. package/dist/src/agent/context/workspace.js.map +1 -1
  99. package/dist/src/agent/embedded/run-for-session.js +4 -1
  100. package/dist/src/agent/embedded/run-for-session.js.map +1 -1
  101. package/dist/src/agent/fallback/candidates.js +10 -11
  102. package/dist/src/agent/fallback/candidates.js.map +1 -1
  103. package/dist/src/agent/memory/builtin-memory-store.d.ts +4 -2
  104. package/dist/src/agent/memory/builtin-memory-store.js +11 -6
  105. package/dist/src/agent/memory/builtin-memory-store.js.map +1 -1
  106. package/dist/src/agent/memory/builtin-provider.js +36 -7
  107. package/dist/src/agent/memory/builtin-provider.js.map +1 -1
  108. package/dist/src/agent/memory/candidate-extractor.d.ts +6 -0
  109. package/dist/src/agent/memory/candidate-extractor.js +60 -0
  110. package/dist/src/agent/memory/candidate-extractor.js.map +1 -0
  111. package/dist/src/agent/memory/manager.d.ts +2 -1
  112. package/dist/src/agent/memory/manager.js +27 -3
  113. package/dist/src/agent/memory/manager.js.map +1 -1
  114. package/dist/src/agent/memory/memory-config.js +3 -0
  115. package/dist/src/agent/memory/memory-config.js.map +1 -1
  116. package/dist/src/agent/memory/prefetch-coordinator.js.map +1 -1
  117. package/dist/src/agent/memory/types.d.ts +27 -4
  118. package/dist/src/agent/prompt/memory/index.d.ts +4 -2
  119. package/dist/src/agent/prompt/memory/index.js +18 -12
  120. package/dist/src/agent/prompt/memory/index.js.map +1 -1
  121. package/dist/src/agent/prompt/sections/memory-skills.js +1 -1
  122. package/dist/src/agent/prompt/sections/memory-skills.js.map +1 -1
  123. package/dist/src/agent/prompt/service-prompt-builder.js +4 -19
  124. package/dist/src/agent/prompt/service-prompt-builder.js.map +1 -1
  125. package/dist/src/agent/reply/post-compaction-context.js +1 -3
  126. package/dist/src/agent/reply/post-compaction-context.js.map +1 -1
  127. package/dist/src/agent/reply/startup-context.js +1 -5
  128. package/dist/src/agent/reply/startup-context.js.map +1 -1
  129. package/dist/src/agent/service.d.ts +3 -3
  130. package/dist/src/agent/service.js +7 -8
  131. package/dist/src/agent/service.js.map +1 -1
  132. package/dist/src/agent/tools/curated-memory-tool.js +57 -2
  133. package/dist/src/agent/tools/curated-memory-tool.js.map +1 -1
  134. package/dist/src/agent/tools/web-extract.js +6 -8
  135. package/dist/src/agent/tools/web-extract.js.map +1 -1
  136. package/dist/src/agent-manifest/index.js +2 -2
  137. package/dist/src/agent-manifest/resolver.d.ts +3 -2
  138. package/dist/src/agent-manifest/resolver.js +5 -3
  139. package/dist/src/agent-manifest/resolver.js.map +1 -1
  140. package/dist/src/agent-manifest/schema.d.ts +232 -0
  141. package/dist/src/agent-manifest/schema.js +4 -2
  142. package/dist/src/agent-manifest/schema.js.map +1 -1
  143. package/dist/src/agent-manifest/validator.d.ts +3 -2
  144. package/dist/src/agent-manifest/validator.js +2 -1
  145. package/dist/src/agent-manifest/validator.js.map +1 -1
  146. package/dist/src/channels/pipeline.d.ts +1 -1
  147. package/dist/src/channels/pipeline.js.map +1 -1
  148. package/dist/src/chat-commands/agent-edit.js +0 -1
  149. package/dist/src/chat-commands/agent-edit.js.map +1 -1
  150. package/dist/src/chat-commands/builtins/model.js +2 -2
  151. package/dist/src/chat-commands/builtins/model.js.map +1 -1
  152. package/dist/src/cli/command-manifest.d.ts +1 -1
  153. package/dist/src/cli/command-manifest.js +1 -1
  154. package/dist/src/cli/command-manifest.js.map +1 -1
  155. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  156. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -0
  157. package/dist/src/cli/commands/doctor/checks/provider-auth.js.map +1 -1
  158. package/dist/src/cli/commands/doctor/checks/workspace-status.js +0 -1
  159. package/dist/src/cli/commands/doctor/checks/workspace-status.js.map +1 -1
  160. package/dist/src/cli/commands/init.js +7 -6
  161. package/dist/src/cli/commands/init.js.map +1 -1
  162. package/dist/src/cli/commands/models.js +13 -31
  163. package/dist/src/cli/commands/models.js.map +1 -1
  164. package/dist/src/cli/commands/onboard/model.js +17 -10
  165. package/dist/src/cli/commands/onboard/model.js.map +1 -1
  166. package/dist/src/cli/commands/onboard.js +2 -2
  167. package/dist/src/cli/commands/onboard.js.map +1 -1
  168. package/dist/src/cli/commands/tui.js +2 -2
  169. package/dist/src/cli/commands/tui.js.map +1 -1
  170. package/dist/src/cli/index.d.ts +1 -0
  171. package/dist/src/cli/index.js +10 -5
  172. package/dist/src/cli/index.js.map +1 -1
  173. package/dist/src/cli/registry.js +1 -1
  174. package/dist/src/cli/registry.js.map +1 -1
  175. package/dist/src/cli/templates.d.ts +1 -1
  176. package/dist/src/cli/templates.js +1 -11
  177. package/dist/src/cli/templates.js.map +1 -1
  178. package/dist/src/commands/agents.config.js +2 -10
  179. package/dist/src/commands/agents.config.js.map +1 -1
  180. package/dist/src/config/agent-profile.js +3 -2
  181. package/dist/src/config/agent-profile.js.map +1 -1
  182. package/dist/src/config/agent-typed-models.js +3 -5
  183. package/dist/src/config/agent-typed-models.js.map +1 -1
  184. package/dist/src/config/index.js +2 -2
  185. package/dist/src/config/paths.d.ts +8 -1
  186. package/dist/src/config/paths.js +16 -3
  187. package/dist/src/config/paths.js.map +1 -1
  188. package/dist/src/config/schema.d.ts +32 -32
  189. package/dist/src/config/schema.js +46 -26
  190. package/dist/src/config/schema.js.map +1 -1
  191. package/dist/src/gateway/agents-admin.d.ts +7 -0
  192. package/dist/src/gateway/agents-admin.js +51 -20
  193. package/dist/src/gateway/agents-admin.js.map +1 -1
  194. package/dist/src/gateway/chat-stream/mapper.js +9 -2
  195. package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
  196. package/dist/src/gateway/global-defaults-admin.d.ts +36 -0
  197. package/dist/src/gateway/global-defaults-admin.js +146 -0
  198. package/dist/src/gateway/global-defaults-admin.js.map +1 -0
  199. package/dist/src/gateway/hono/routes/agents.js +33 -1
  200. package/dist/src/gateway/hono/routes/agents.js.map +1 -1
  201. package/dist/src/gateway/hono/routes/commands-skills.js +4 -5
  202. package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
  203. package/dist/src/gateway/hono/routes/global-defaults.d.ts +3 -0
  204. package/dist/src/gateway/hono/routes/global-defaults.js +61 -0
  205. package/dist/src/gateway/hono/routes/global-defaults.js.map +1 -0
  206. package/dist/src/gateway/hono/routes/lazy-bundles.js +11 -3
  207. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  208. package/dist/src/gateway/hono/routes/memory.js +75 -4
  209. package/dist/src/gateway/hono/routes/memory.js.map +1 -1
  210. package/dist/src/gateway/hono/routes/models.js +1 -1
  211. package/dist/src/gateway/service/marketplace-service.d.ts +4 -4
  212. package/dist/src/gateway/service/marketplace-service.js +11 -7
  213. package/dist/src/gateway/service/marketplace-service.js.map +1 -1
  214. package/dist/src/notes/service.js +3 -1
  215. package/dist/src/notes/service.js.map +1 -1
  216. package/dist/src/providers/index.d.ts +4 -5
  217. package/dist/src/providers/index.js +16 -16
  218. package/dist/src/providers/index.js.map +1 -1
  219. package/dist/src/session/session-workspace.js +2 -7
  220. package/dist/src/session/session-workspace.js.map +1 -1
  221. package/dist/src/session/store.js +0 -5
  222. package/dist/src/session/store.js.map +1 -1
  223. package/dist/src/storage/sqlite/index.d.ts +1 -1
  224. package/dist/src/storage/sqlite/index.js +2 -2
  225. package/dist/src/storage/sqlite/memory-index-repository.d.ts +1 -0
  226. package/dist/src/storage/sqlite/memory-index-repository.js +15 -14
  227. package/dist/src/storage/sqlite/memory-index-repository.js.map +1 -1
  228. package/dist/src/storage/sqlite/memory-records-repository.d.ts +44 -1
  229. package/dist/src/storage/sqlite/memory-records-repository.js +164 -20
  230. package/dist/src/storage/sqlite/memory-records-repository.js.map +1 -1
  231. package/dist/src/storage/sqlite/migrations/014_memory_record_governance.sql +10 -0
  232. package/dist/src/storage/sqlite/migrations/015_memory_trace_feedback.sql +4 -0
  233. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  234. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  235. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  236. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  237. package/dist/src/storage/sqlite/schema.js +1 -1
  238. package/dist/src/tui/backends/embedded-backend.d.ts +3 -0
  239. package/dist/src/tui/backends/embedded-backend.js +37 -20
  240. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  241. package/dist/src/tui/backends/gateway-sse-backend.d.ts +1 -0
  242. package/dist/src/tui/backends/gateway-sse-backend.js +16 -3
  243. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
  244. package/dist/src/tui/components/tui-header.d.ts +0 -1
  245. package/dist/src/tui/components/tui-header.js +3 -8
  246. package/dist/src/tui/components/tui-header.js.map +1 -1
  247. package/dist/src/tui/tui-startup-resources.js +2 -1
  248. package/dist/src/tui/tui-startup-resources.js.map +1 -1
  249. package/dist/src/tui/tui-startup-text.js +12 -12
  250. package/dist/src/tui/tui-startup-text.js.map +1 -1
  251. package/dist/src/tui/tui.js +4 -1
  252. package/dist/src/tui/tui.js.map +1 -1
  253. package/package.json +1 -1
  254. package/dist/gateway/static/root/assets/agents-C6xgagZd.js +0 -71
  255. package/dist/gateway/static/root/assets/browser-settings-page-CUOO-PQk.js +0 -8
  256. package/dist/gateway/static/root/assets/capability-presets-settings-panel-CfqF-kGr.js +0 -1
  257. package/dist/gateway/static/root/assets/channels-settings-BYyRzsex.js +0 -1
  258. package/dist/gateway/static/root/assets/connectors-page-DIvEz4NX.js +0 -2
  259. package/dist/gateway/static/root/assets/cron-page-D97nFqQD.js +0 -3
  260. package/dist/gateway/static/root/assets/goal-detail-page-DCUROLMQ.js +0 -1
  261. package/dist/gateway/static/root/assets/heartbeat-settings-CC69vNx0.js +0 -1
  262. package/dist/gateway/static/root/assets/index-CyfhARL-.css +0 -1
  263. package/dist/gateway/static/root/assets/messages-Cf1O9JB0.js +0 -3
  264. package/dist/gateway/static/root/assets/models-hub-panel-DmKiIihT.js +0 -2
  265. package/dist/gateway/static/root/assets/note-detail-page-CxxfLRJZ.js +0 -2
  266. package/dist/gateway/static/root/assets/notes-page-X2slSx7V.js +0 -1
  267. package/dist/gateway/static/root/assets/remote-access-hub-BvmUWqgu.js +0 -1
  268. package/dist/gateway/static/root/assets/sessions-page-B0yESuuL.js +0 -1
  269. package/dist/gateway/static/root/assets/settings-advanced-gate-q2M8RKOo.js +0 -1
  270. package/dist/gateway/static/root/assets/settings-page-CGozabLD.js +0 -1
  271. package/dist/gateway/static/root/assets/setup-status-panel-FoOSe6nU.js +0 -1
  272. package/dist/gateway/static/root/assets/shares-settings-Cy5zWyYQ.js +0 -1
  273. package/dist/gateway/static/root/assets/skill-reload-api-DaZNFro9.js +0 -1
  274. package/dist/gateway/static/root/assets/skills-page-CnwtUzvw.js +0 -2
  275. package/dist/gateway/static/root/assets/voice-api-key-field-DvKiOglO.js +0 -1
  276. package/dist/gateway/static/root/assets/workflows-page-CAVZcONQ.js +0 -18
  277. package/dist/src/agent/context/workspace-templates/USER.md +0 -24
@@ -1 +1 @@
1
- {"version":3,"file":"bootstrap-files.js","names":[],"sources":["../../../../src/agent/bootstrap/bootstrap-files.ts"],"sourcesContent":["import type { Config } from '../../config/schema.js';\nimport { DEFAULT_HEARTBEAT_FILENAME } from '../context/workspace.js';\nimport {\n getOrLoadBootstrapFiles,\n markBootstrapContextInjected,\n wasBootstrapContextInjected,\n} from './bootstrap-cache.js';\nimport {\n buildBootstrapContextFiles,\n resolveBootstrapMaxChars,\n resolveBootstrapTotalMaxChars,\n} from './bootstrap-context.js';\nimport { filterBootstrapFilesForSession } from './filter-bootstrap-files.js';\nimport { loadProfileBootstrapFiles } from './load-bootstrap-files.js';\nimport type { EmbeddedContextFile, WorkspaceBootstrapFile } from './types.js';\n\nexport { clearAllBootstrapSnapshots, clearBootstrapSnapshot } from './bootstrap-cache.js';\n\nfunction filterHeartbeatBootstrapFile(\n files: WorkspaceBootstrapFile[],\n exclude: boolean,\n): WorkspaceBootstrapFile[] {\n if (!exclude) {\n return files;\n }\n return files.filter((file) => file.name !== DEFAULT_HEARTBEAT_FILENAME);\n}\n\nexport function resolveBootstrapFilesSync(params: {\n profileDir: string;\n sessionKey?: string;\n excludeHeartbeat?: boolean;\n}): WorkspaceBootstrapFile[] {\n const rawFiles = loadProfileBootstrapFiles(params.profileDir);\n const filtered = filterBootstrapFilesForSession(rawFiles, params.sessionKey);\n return filterHeartbeatBootstrapFile(filtered, params.excludeHeartbeat ?? false);\n}\n\nexport async function resolveBootstrapFilesForRun(params: {\n profileDir: string;\n sessionKey?: string;\n excludeHeartbeat?: boolean;\n warn?: (message: string) => void;\n}): Promise<WorkspaceBootstrapFile[]> {\n const sessionKey = params.sessionKey;\n const rawFiles = sessionKey\n ? await getOrLoadBootstrapFiles({\n profileDir: params.profileDir,\n sessionKey,\n })\n : loadProfileBootstrapFiles(params.profileDir);\n const filtered = filterBootstrapFilesForSession(rawFiles, sessionKey);\n return filterHeartbeatBootstrapFile(filtered, params.excludeHeartbeat ?? false);\n}\n\nexport function resolveBootstrapContextSync(params: {\n profileDir: string;\n config?: Config;\n sessionKey?: string;\n excludeHeartbeat?: boolean;\n contextInjection?: 'always' | 'continuation-skip' | 'never';\n}): {\n bootstrapFiles: WorkspaceBootstrapFile[];\n contextFiles: EmbeddedContextFile[];\n} {\n const mode = params.contextInjection ?? 'always';\n if (mode === 'never') {\n return { bootstrapFiles: [], contextFiles: [] };\n }\n if (\n mode === 'continuation-skip' &&\n params.sessionKey &&\n wasBootstrapContextInjected(params.sessionKey)\n ) {\n return { bootstrapFiles: [], contextFiles: [] };\n }\n const bootstrapFiles = resolveBootstrapFilesSync(params);\n const contextFiles = buildBootstrapContextFiles(bootstrapFiles, {\n maxChars: resolveBootstrapMaxChars(params.config),\n totalMaxChars: resolveBootstrapTotalMaxChars(params.config),\n });\n if (mode === 'continuation-skip' && params.sessionKey && contextFiles.length > 0) {\n markBootstrapContextInjected(params.sessionKey);\n }\n return { bootstrapFiles, contextFiles };\n}\n\nexport async function resolveBootstrapContextForRun(params: {\n profileDir: string;\n config?: Config;\n sessionKey?: string;\n excludeHeartbeat?: boolean;\n contextInjection?: 'always' | 'continuation-skip' | 'never';\n warn?: (message: string) => void;\n}): Promise<{\n bootstrapFiles: WorkspaceBootstrapFile[];\n contextFiles: EmbeddedContextFile[];\n}> {\n const mode = params.contextInjection ?? 'always';\n if (mode === 'never') {\n return { bootstrapFiles: [], contextFiles: [] };\n }\n if (\n mode === 'continuation-skip' &&\n params.sessionKey &&\n wasBootstrapContextInjected(params.sessionKey)\n ) {\n return { bootstrapFiles: [], contextFiles: [] };\n }\n const bootstrapFiles = await resolveBootstrapFilesForRun(params);\n const contextFiles = buildBootstrapContextFiles(bootstrapFiles, {\n maxChars: resolveBootstrapMaxChars(params.config),\n totalMaxChars: resolveBootstrapTotalMaxChars(params.config),\n warn: params.warn,\n });\n if (mode === 'continuation-skip' && params.sessionKey && contextFiles.length > 0) {\n markBootstrapContextInjected(params.sessionKey);\n }\n return { bootstrapFiles, contextFiles };\n}\n"],"mappings":";;;;;;AAkBA,SAAS,6BACP,OACA,SAC0B;AAC1B,KAAI,CAAC,QACH,QAAO;AAET,QAAO,MAAM,QAAQ,SAAS,KAAK,SAAS,2BAA2B;;AAGzE,SAAgB,0BAA0B,QAIb;AAG3B,QAAO,6BADU,+BADA,0BAA0B,OAAO,WACM,EAAE,OAAO,WACrB,EAAE,OAAO,oBAAoB,MAAM;;AAGjF,eAAsB,4BAA4B,QAKZ;CACpC,MAAM,aAAa,OAAO;AAQ1B,QAAO,6BADU,+BANA,aACb,MAAM,wBAAwB;EAC5B,YAAY,OAAO;EACnB;EACD,CAAC,GACF,0BAA0B,OAAO,WAAW,EACU,WACd,EAAE,OAAO,oBAAoB,MAAM;;AAGjF,SAAgB,4BAA4B,QAS1C;CACA,MAAM,OAAO,OAAO,oBAAoB;AACxC,KAAI,SAAS,QACX,QAAO;EAAE,gBAAgB,EAAE;EAAE,cAAc,EAAE;EAAE;AAEjD,KACE,SAAS,uBACT,OAAO,cACP,4BAA4B,OAAO,WAAW,CAE9C,QAAO;EAAE,gBAAgB,EAAE;EAAE,cAAc,EAAE;EAAE;CAEjD,MAAM,iBAAiB,0BAA0B,OAAO;CACxD,MAAM,eAAe,2BAA2B,gBAAgB;EAC9D,UAAU,yBAAyB,OAAO,OAAO;EACjD,eAAe,8BAA8B,OAAO,OAAO;EAC5D,CAAC;AACF,KAAI,SAAS,uBAAuB,OAAO,cAAc,aAAa,SAAS,EAC7E,8BAA6B,OAAO,WAAW;AAEjD,QAAO;EAAE;EAAgB;EAAc;;AAGzC,eAAsB,8BAA8B,QAUjD;CACD,MAAM,OAAO,OAAO,oBAAoB;AACxC,KAAI,SAAS,QACX,QAAO;EAAE,gBAAgB,EAAE;EAAE,cAAc,EAAE;EAAE;AAEjD,KACE,SAAS,uBACT,OAAO,cACP,4BAA4B,OAAO,WAAW,CAE9C,QAAO;EAAE,gBAAgB,EAAE;EAAE,cAAc,EAAE;EAAE;CAEjD,MAAM,iBAAiB,MAAM,4BAA4B,OAAO;CAChE,MAAM,eAAe,2BAA2B,gBAAgB;EAC9D,UAAU,yBAAyB,OAAO,OAAO;EACjD,eAAe,8BAA8B,OAAO,OAAO;EAC3D,MAAM,OAAO;EACd,CAAC;AACF,KAAI,SAAS,uBAAuB,OAAO,cAAc,aAAa,SAAS,EAC7E,8BAA6B,OAAO,WAAW;AAEjD,QAAO;EAAE;EAAgB;EAAc"}
1
+ {"version":3,"file":"bootstrap-files.js","names":[],"sources":["../../../../src/agent/bootstrap/bootstrap-files.ts"],"sourcesContent":["import type { Config } from '../../config/schema.js';\nimport { DEFAULT_HEARTBEAT_FILENAME } from '../context/workspace.js';\nimport {\n getOrLoadBootstrapFiles,\n markBootstrapContextInjected,\n wasBootstrapContextInjected,\n} from './bootstrap-cache.js';\nimport {\n buildBootstrapContextFiles,\n resolveBootstrapMaxChars,\n resolveBootstrapTotalMaxChars,\n} from './bootstrap-context.js';\nimport { filterBootstrapFilesForSession } from './filter-bootstrap-files.js';\nimport { loadProfileBootstrapFiles, loadUserProfileBootstrapFile } from './load-bootstrap-files.js';\nimport type { EmbeddedContextFile, WorkspaceBootstrapFile } from './types.js';\n\nexport { clearAllBootstrapSnapshots, clearBootstrapSnapshot } from './bootstrap-cache.js';\n\nfunction filterHeartbeatBootstrapFile(\n files: WorkspaceBootstrapFile[],\n exclude: boolean,\n): WorkspaceBootstrapFile[] {\n if (!exclude) {\n return files;\n }\n return files.filter((file) => file.name !== DEFAULT_HEARTBEAT_FILENAME);\n}\n\nexport function resolveBootstrapFilesSync(params: {\n profileDir: string;\n userProfilePath?: string;\n sessionKey?: string;\n excludeHeartbeat?: boolean;\n}): WorkspaceBootstrapFile[] {\n const rawFiles = [\n ...loadUserProfileBootstrapFile(params.userProfilePath),\n ...loadProfileBootstrapFiles(params.profileDir),\n ];\n const filtered = filterBootstrapFilesForSession(rawFiles, params.sessionKey);\n return filterHeartbeatBootstrapFile(filtered, params.excludeHeartbeat ?? false);\n}\n\nexport async function resolveBootstrapFilesForRun(params: {\n profileDir: string;\n userProfilePath?: string;\n sessionKey?: string;\n excludeHeartbeat?: boolean;\n warn?: (message: string) => void;\n}): Promise<WorkspaceBootstrapFile[]> {\n const sessionKey = params.sessionKey;\n const rawFiles = sessionKey\n ? await getOrLoadBootstrapFiles({\n profileDir: params.profileDir,\n userProfilePath: params.userProfilePath,\n sessionKey,\n })\n : [\n ...loadUserProfileBootstrapFile(params.userProfilePath),\n ...loadProfileBootstrapFiles(params.profileDir),\n ];\n const filtered = filterBootstrapFilesForSession(rawFiles, sessionKey);\n return filterHeartbeatBootstrapFile(filtered, params.excludeHeartbeat ?? false);\n}\n\nexport function resolveBootstrapContextSync(params: {\n profileDir: string;\n userProfilePath?: string;\n config?: Config;\n sessionKey?: string;\n excludeHeartbeat?: boolean;\n contextInjection?: 'always' | 'continuation-skip' | 'never';\n}): {\n bootstrapFiles: WorkspaceBootstrapFile[];\n contextFiles: EmbeddedContextFile[];\n} {\n const mode = params.contextInjection ?? 'always';\n if (mode === 'never') {\n return { bootstrapFiles: [], contextFiles: [] };\n }\n if (\n mode === 'continuation-skip' &&\n params.sessionKey &&\n wasBootstrapContextInjected(params.sessionKey)\n ) {\n return { bootstrapFiles: [], contextFiles: [] };\n }\n const bootstrapFiles = resolveBootstrapFilesSync(params);\n const contextFiles = buildBootstrapContextFiles(bootstrapFiles, {\n maxChars: resolveBootstrapMaxChars(params.config),\n totalMaxChars: resolveBootstrapTotalMaxChars(params.config),\n });\n if (mode === 'continuation-skip' && params.sessionKey && contextFiles.length > 0) {\n markBootstrapContextInjected(params.sessionKey);\n }\n return { bootstrapFiles, contextFiles };\n}\n\nexport async function resolveBootstrapContextForRun(params: {\n profileDir: string;\n userProfilePath?: string;\n config?: Config;\n sessionKey?: string;\n excludeHeartbeat?: boolean;\n contextInjection?: 'always' | 'continuation-skip' | 'never';\n warn?: (message: string) => void;\n}): Promise<{\n bootstrapFiles: WorkspaceBootstrapFile[];\n contextFiles: EmbeddedContextFile[];\n}> {\n const mode = params.contextInjection ?? 'always';\n if (mode === 'never') {\n return { bootstrapFiles: [], contextFiles: [] };\n }\n if (\n mode === 'continuation-skip' &&\n params.sessionKey &&\n wasBootstrapContextInjected(params.sessionKey)\n ) {\n return { bootstrapFiles: [], contextFiles: [] };\n }\n const bootstrapFiles = await resolveBootstrapFilesForRun(params);\n const contextFiles = buildBootstrapContextFiles(bootstrapFiles, {\n maxChars: resolveBootstrapMaxChars(params.config),\n totalMaxChars: resolveBootstrapTotalMaxChars(params.config),\n warn: params.warn,\n });\n if (mode === 'continuation-skip' && params.sessionKey && contextFiles.length > 0) {\n markBootstrapContextInjected(params.sessionKey);\n }\n return { bootstrapFiles, contextFiles };\n}\n"],"mappings":";;;;;;AAkBA,SAAS,6BACP,OACA,SAC0B;AAC1B,KAAI,CAAC,QACH,QAAO;AAET,QAAO,MAAM,QAAQ,SAAS,KAAK,SAAS,2BAA2B;;AAGzE,SAAgB,0BAA0B,QAKb;AAM3B,QAAO,6BADU,+BAA+B,CAH9C,GAAG,6BAA6B,OAAO,gBAAgB,EACvD,GAAG,0BAA0B,OAAO,WAAW,CAEO,EAAE,OAAO,WACrB,EAAE,OAAO,oBAAoB,MAAM;;AAGjF,eAAsB,4BAA4B,QAMZ;CACpC,MAAM,aAAa,OAAO;AAY1B,QAAO,6BADU,+BAVA,aACb,MAAM,wBAAwB;EAC5B,YAAY,OAAO;EACnB,iBAAiB,OAAO;EACxB;EACD,CAAC,GACF,CACE,GAAG,6BAA6B,OAAO,gBAAgB,EACvD,GAAG,0BAA0B,OAAO,WAAW,CAChD,EACqD,WACd,EAAE,OAAO,oBAAoB,MAAM;;AAGjF,SAAgB,4BAA4B,QAU1C;CACA,MAAM,OAAO,OAAO,oBAAoB;AACxC,KAAI,SAAS,QACX,QAAO;EAAE,gBAAgB,EAAE;EAAE,cAAc,EAAE;EAAE;AAEjD,KACE,SAAS,uBACT,OAAO,cACP,4BAA4B,OAAO,WAAW,CAE9C,QAAO;EAAE,gBAAgB,EAAE;EAAE,cAAc,EAAE;EAAE;CAEjD,MAAM,iBAAiB,0BAA0B,OAAO;CACxD,MAAM,eAAe,2BAA2B,gBAAgB;EAC9D,UAAU,yBAAyB,OAAO,OAAO;EACjD,eAAe,8BAA8B,OAAO,OAAO;EAC5D,CAAC;AACF,KAAI,SAAS,uBAAuB,OAAO,cAAc,aAAa,SAAS,EAC7E,8BAA6B,OAAO,WAAW;AAEjD,QAAO;EAAE;EAAgB;EAAc;;AAGzC,eAAsB,8BAA8B,QAWjD;CACD,MAAM,OAAO,OAAO,oBAAoB;AACxC,KAAI,SAAS,QACX,QAAO;EAAE,gBAAgB,EAAE;EAAE,cAAc,EAAE;EAAE;AAEjD,KACE,SAAS,uBACT,OAAO,cACP,4BAA4B,OAAO,WAAW,CAE9C,QAAO;EAAE,gBAAgB,EAAE;EAAE,cAAc,EAAE;EAAE;CAEjD,MAAM,iBAAiB,MAAM,4BAA4B,OAAO;CAChE,MAAM,eAAe,2BAA2B,gBAAgB;EAC9D,UAAU,yBAAyB,OAAO,OAAO;EACjD,eAAe,8BAA8B,OAAO,OAAO;EAC3D,MAAM,OAAO;EACd,CAAC;AACF,KAAI,SAAS,uBAAuB,OAAO,cAAc,aAAa,SAAS,EAC7E,8BAA6B,OAAO,WAAW;AAEjD,QAAO;EAAE;EAAgB;EAAc"}
@@ -1,14 +1,16 @@
1
1
  import { isCronSessionKey, isSubagentSessionKey } from "../../routing/session-key-utils.js";
2
2
  import { init_session_key } from "../../routing/session-key.js";
3
- import { DEFAULT_AGENTS_FILENAME, DEFAULT_IDENTITY_FILENAME, DEFAULT_SOUL_FILENAME, DEFAULT_TOOLS_FILENAME, DEFAULT_USER_FILENAME } from "../context/workspace.js";
3
+ import { USER_PROFILE_FILENAME, init_paths } from "../../config/paths.js";
4
+ import { DEFAULT_AGENTS_FILENAME, DEFAULT_IDENTITY_FILENAME, DEFAULT_SOUL_FILENAME, DEFAULT_TOOLS_FILENAME } from "../context/workspace.js";
4
5
  //#region src/agent/bootstrap/filter-bootstrap-files.ts
6
+ init_paths();
5
7
  init_session_key();
6
8
  const MINIMAL_BOOTSTRAP_ALLOWLIST = new Set([
7
9
  DEFAULT_AGENTS_FILENAME,
8
10
  DEFAULT_TOOLS_FILENAME,
9
11
  DEFAULT_SOUL_FILENAME,
10
12
  DEFAULT_IDENTITY_FILENAME,
11
- DEFAULT_USER_FILENAME
13
+ USER_PROFILE_FILENAME
12
14
  ]);
13
15
  /** Subagent and cron sessions omit MEMORY.md and other heavy profile files. */
14
16
  function filterBootstrapFilesForSession(files, sessionKey) {
@@ -1 +1 @@
1
- {"version":3,"file":"filter-bootstrap-files.js","names":[],"sources":["../../../../src/agent/bootstrap/filter-bootstrap-files.ts"],"sourcesContent":["import { isCronSessionKey, isSubagentSessionKey } from '../../routing/session-key.js';\nimport {\n DEFAULT_AGENTS_FILENAME,\n DEFAULT_IDENTITY_FILENAME,\n DEFAULT_SOUL_FILENAME,\n DEFAULT_TOOLS_FILENAME,\n DEFAULT_USER_FILENAME,\n} from '../context/workspace.js';\nimport type { WorkspaceBootstrapFile } from './types.js';\n\nconst MINIMAL_BOOTSTRAP_ALLOWLIST = new Set<string>([\n DEFAULT_AGENTS_FILENAME,\n DEFAULT_TOOLS_FILENAME,\n DEFAULT_SOUL_FILENAME,\n DEFAULT_IDENTITY_FILENAME,\n DEFAULT_USER_FILENAME,\n]);\n\n/** Subagent and cron sessions omit MEMORY.md and other heavy profile files. */\nexport function filterBootstrapFilesForSession(\n files: WorkspaceBootstrapFile[],\n sessionKey?: string,\n): WorkspaceBootstrapFile[] {\n if (!sessionKey || (!isSubagentSessionKey(sessionKey) && !isCronSessionKey(sessionKey))) {\n return files;\n }\n return files.filter((file) => MINIMAL_BOOTSTRAP_ALLOWLIST.has(file.name));\n}\n"],"mappings":";;;;kBAAsF;AAUtF,MAAM,8BAA8B,IAAI,IAAY;CAClD;CACA;CACA;CACA;CACA;CACD,CAAC;;AAGF,SAAgB,+BACd,OACA,YAC0B;AAC1B,KAAI,CAAC,cAAe,CAAC,qBAAqB,WAAW,IAAI,CAAC,iBAAiB,WAAW,CACpF,QAAO;AAET,QAAO,MAAM,QAAQ,SAAS,4BAA4B,IAAI,KAAK,KAAK,CAAC"}
1
+ {"version":3,"file":"filter-bootstrap-files.js","names":[],"sources":["../../../../src/agent/bootstrap/filter-bootstrap-files.ts"],"sourcesContent":["import { USER_PROFILE_FILENAME } from '../../config/paths.js';\nimport { isCronSessionKey, isSubagentSessionKey } from '../../routing/session-key.js';\nimport {\n DEFAULT_AGENTS_FILENAME,\n DEFAULT_IDENTITY_FILENAME,\n DEFAULT_SOUL_FILENAME,\n DEFAULT_TOOLS_FILENAME,\n} from '../context/workspace.js';\nimport type { WorkspaceBootstrapFile } from './types.js';\n\nconst MINIMAL_BOOTSTRAP_ALLOWLIST = new Set<string>([\n DEFAULT_AGENTS_FILENAME,\n DEFAULT_TOOLS_FILENAME,\n DEFAULT_SOUL_FILENAME,\n DEFAULT_IDENTITY_FILENAME,\n USER_PROFILE_FILENAME,\n]);\n\n/** Subagent and cron sessions omit MEMORY.md and other heavy profile files. */\nexport function filterBootstrapFilesForSession(\n files: WorkspaceBootstrapFile[],\n sessionKey?: string,\n): WorkspaceBootstrapFile[] {\n if (!sessionKey || (!isSubagentSessionKey(sessionKey) && !isCronSessionKey(sessionKey))) {\n return files;\n }\n return files.filter((file) => MINIMAL_BOOTSTRAP_ALLOWLIST.has(file.name));\n}\n"],"mappings":";;;;;YAA8D;kBACwB;AAStF,MAAM,8BAA8B,IAAI,IAAY;CAClD;CACA;CACA;CACA;CACA;CACD,CAAC;;AAGF,SAAgB,+BACd,OACA,YAC0B;AAC1B,KAAI,CAAC,cAAe,CAAC,qBAAqB,WAAW,IAAI,CAAC,iBAAiB,WAAW,CACpF,QAAO;AAET,QAAO,MAAM,QAAQ,SAAS,4BAA4B,IAAI,KAAK,KAAK,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import type { WorkspaceBootstrapFile } from './types.js';
2
+ export declare function loadUserProfileBootstrapFile(userProfilePath: string | undefined): WorkspaceBootstrapFile[];
2
3
  /**
3
4
  * Load bootstrap profile Markdown from `agents/<id>/profile/`.
4
- * MEMORY.md is omitted when absent; other slots emit missing markers.
5
+ * Required files emit missing markers; optional files are omitted when absent.
5
6
  */
6
7
  export declare function loadProfileBootstrapFiles(profileDir: string): WorkspaceBootstrapFile[];
@@ -1,13 +1,14 @@
1
- import { DEFAULT_AGENTS_FILENAME, DEFAULT_HEARTBEAT_FILENAME, DEFAULT_IDENTITY_FILENAME, DEFAULT_MEMORY_FILENAME, DEFAULT_SOUL_FILENAME, DEFAULT_TOOLS_FILENAME, DEFAULT_USER_FILENAME, stripFrontMatter } from "../context/workspace.js";
1
+ import { USER_PROFILE_FILENAME, init_paths } from "../../config/paths.js";
2
+ import { DEFAULT_AGENTS_FILENAME, DEFAULT_HEARTBEAT_FILENAME, DEFAULT_IDENTITY_FILENAME, DEFAULT_MEMORY_FILENAME, DEFAULT_SOUL_FILENAME, DEFAULT_TOOLS_FILENAME, REQUIRED_AGENT_PROFILE_MARKDOWN_FILE_SET, stripFrontMatter } from "../context/workspace.js";
2
3
  import { join, resolve } from "node:path";
3
4
  import { existsSync, readFileSync } from "node:fs";
4
5
  //#region src/agent/bootstrap/load-bootstrap-files.ts
6
+ init_paths();
5
7
  const PROFILE_LOAD_ORDER = [
6
8
  DEFAULT_AGENTS_FILENAME,
7
9
  DEFAULT_SOUL_FILENAME,
8
10
  DEFAULT_TOOLS_FILENAME,
9
11
  DEFAULT_IDENTITY_FILENAME,
10
- DEFAULT_USER_FILENAME,
11
12
  DEFAULT_HEARTBEAT_FILENAME,
12
13
  DEFAULT_MEMORY_FILENAME
13
14
  ];
@@ -19,26 +20,26 @@ function readProfileFile(filePath) {
19
20
  return null;
20
21
  }
21
22
  }
23
+ function loadUserProfileBootstrapFile(userProfilePath) {
24
+ if (!userProfilePath) return [];
25
+ const filePath = resolve(userProfilePath);
26
+ const content = readProfileFile(filePath);
27
+ return content !== null ? [{
28
+ name: USER_PROFILE_FILENAME,
29
+ path: filePath,
30
+ content,
31
+ missing: false
32
+ }] : [];
33
+ }
22
34
  /**
23
35
  * Load bootstrap profile Markdown from `agents/<id>/profile/`.
24
- * MEMORY.md is omitted when absent; other slots emit missing markers.
36
+ * Required files emit missing markers; optional files are omitted when absent.
25
37
  */
26
38
  function loadProfileBootstrapFiles(profileDir) {
27
39
  const resolvedDir = resolve(profileDir);
28
40
  const result = [];
29
41
  for (const name of PROFILE_LOAD_ORDER) {
30
42
  const filePath = join(resolvedDir, name);
31
- if (name === "MEMORY.md") {
32
- const content = readProfileFile(filePath);
33
- if (content === null) continue;
34
- result.push({
35
- name,
36
- path: filePath,
37
- content,
38
- missing: false
39
- });
40
- continue;
41
- }
42
43
  const content = readProfileFile(filePath);
43
44
  if (content !== null) result.push({
44
45
  name,
@@ -46,7 +47,7 @@ function loadProfileBootstrapFiles(profileDir) {
46
47
  content,
47
48
  missing: false
48
49
  });
49
- else result.push({
50
+ else if (REQUIRED_AGENT_PROFILE_MARKDOWN_FILE_SET.has(name)) result.push({
50
51
  name,
51
52
  path: filePath,
52
53
  missing: true
@@ -55,6 +56,6 @@ function loadProfileBootstrapFiles(profileDir) {
55
56
  return result;
56
57
  }
57
58
  //#endregion
58
- export { loadProfileBootstrapFiles };
59
+ export { loadProfileBootstrapFiles, loadUserProfileBootstrapFile };
59
60
 
60
61
  //# sourceMappingURL=load-bootstrap-files.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"load-bootstrap-files.js","names":[],"sources":["../../../../src/agent/bootstrap/load-bootstrap-files.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\n\nimport { stripFrontMatter } from '../context/workspace.js';\nimport {\n DEFAULT_AGENTS_FILENAME,\n DEFAULT_HEARTBEAT_FILENAME,\n DEFAULT_IDENTITY_FILENAME,\n DEFAULT_MEMORY_FILENAME,\n DEFAULT_SOUL_FILENAME,\n DEFAULT_TOOLS_FILENAME,\n DEFAULT_USER_FILENAME,\n} from '../context/workspace.js';\nimport type { BootstrapFileName, WorkspaceBootstrapFile } from './types.js';\n\nconst PROFILE_LOAD_ORDER: BootstrapFileName[] = [\n DEFAULT_AGENTS_FILENAME,\n DEFAULT_SOUL_FILENAME,\n DEFAULT_TOOLS_FILENAME,\n DEFAULT_IDENTITY_FILENAME,\n DEFAULT_USER_FILENAME,\n DEFAULT_HEARTBEAT_FILENAME,\n DEFAULT_MEMORY_FILENAME,\n];\n\nfunction readProfileFile(filePath: string): string | null {\n if (!existsSync(filePath)) {\n return null;\n }\n try {\n return stripFrontMatter(readFileSync(filePath, 'utf-8'));\n } catch {\n return null;\n }\n}\n\n/**\n * Load bootstrap profile Markdown from `agents/<id>/profile/`.\n * MEMORY.md is omitted when absent; other slots emit missing markers.\n */\nexport function loadProfileBootstrapFiles(profileDir: string): WorkspaceBootstrapFile[] {\n const resolvedDir = resolve(profileDir);\n const result: WorkspaceBootstrapFile[] = [];\n\n for (const name of PROFILE_LOAD_ORDER) {\n const filePath = join(resolvedDir, name);\n\n if (name === DEFAULT_MEMORY_FILENAME) {\n const content = readProfileFile(filePath);\n if (content === null) {\n continue;\n }\n result.push({ name, path: filePath, content, missing: false });\n continue;\n }\n\n const content = readProfileFile(filePath);\n if (content !== null) {\n result.push({ name, path: filePath, content, missing: false });\n } else {\n result.push({ name, path: filePath, missing: true });\n }\n }\n\n return result;\n}\n"],"mappings":";;;;AAeA,MAAM,qBAA0C;CAC9C;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,SAAS,gBAAgB,UAAiC;AACxD,KAAI,CAAC,WAAW,SAAS,CACvB,QAAO;AAET,KAAI;AACF,SAAO,iBAAiB,aAAa,UAAU,QAAQ,CAAC;SAClD;AACN,SAAO;;;;;;;AAQX,SAAgB,0BAA0B,YAA8C;CACtF,MAAM,cAAc,QAAQ,WAAW;CACvC,MAAM,SAAmC,EAAE;AAE3C,MAAK,MAAM,QAAQ,oBAAoB;EACrC,MAAM,WAAW,KAAK,aAAa,KAAK;AAExC,MAAI,SAAA,aAAkC;GACpC,MAAM,UAAU,gBAAgB,SAAS;AACzC,OAAI,YAAY,KACd;AAEF,UAAO,KAAK;IAAE;IAAM,MAAM;IAAU;IAAS,SAAS;IAAO,CAAC;AAC9D;;EAGF,MAAM,UAAU,gBAAgB,SAAS;AACzC,MAAI,YAAY,KACd,QAAO,KAAK;GAAE;GAAM,MAAM;GAAU;GAAS,SAAS;GAAO,CAAC;MAE9D,QAAO,KAAK;GAAE;GAAM,MAAM;GAAU,SAAS;GAAM,CAAC;;AAIxD,QAAO"}
1
+ {"version":3,"file":"load-bootstrap-files.js","names":[],"sources":["../../../../src/agent/bootstrap/load-bootstrap-files.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\n\nimport { USER_PROFILE_FILENAME } from '../../config/paths.js';\nimport { stripFrontMatter } from '../context/workspace.js';\nimport {\n DEFAULT_AGENTS_FILENAME,\n DEFAULT_HEARTBEAT_FILENAME,\n DEFAULT_IDENTITY_FILENAME,\n DEFAULT_MEMORY_FILENAME,\n DEFAULT_SOUL_FILENAME,\n DEFAULT_TOOLS_FILENAME,\n REQUIRED_AGENT_PROFILE_MARKDOWN_FILE_SET,\n} from '../context/workspace.js';\nimport type { BootstrapFileName, WorkspaceBootstrapFile } from './types.js';\n\nconst PROFILE_LOAD_ORDER: BootstrapFileName[] = [\n DEFAULT_AGENTS_FILENAME,\n DEFAULT_SOUL_FILENAME,\n DEFAULT_TOOLS_FILENAME,\n DEFAULT_IDENTITY_FILENAME,\n DEFAULT_HEARTBEAT_FILENAME,\n DEFAULT_MEMORY_FILENAME,\n];\n\nfunction readProfileFile(filePath: string): string | null {\n if (!existsSync(filePath)) {\n return null;\n }\n try {\n return stripFrontMatter(readFileSync(filePath, 'utf-8'));\n } catch {\n return null;\n }\n}\n\nexport function loadUserProfileBootstrapFile(userProfilePath: string | undefined): WorkspaceBootstrapFile[] {\n if (!userProfilePath) {\n return [];\n }\n const filePath = resolve(userProfilePath);\n const content = readProfileFile(filePath);\n return content !== null\n ? [{ name: USER_PROFILE_FILENAME, path: filePath, content, missing: false }]\n : [];\n}\n\n/**\n * Load bootstrap profile Markdown from `agents/<id>/profile/`.\n * Required files emit missing markers; optional files are omitted when absent.\n */\nexport function loadProfileBootstrapFiles(profileDir: string): WorkspaceBootstrapFile[] {\n const resolvedDir = resolve(profileDir);\n const result: WorkspaceBootstrapFile[] = [];\n\n for (const name of PROFILE_LOAD_ORDER) {\n const filePath = join(resolvedDir, name);\n\n const content = readProfileFile(filePath);\n if (content !== null) {\n result.push({ name, path: filePath, content, missing: false });\n } else if (REQUIRED_AGENT_PROFILE_MARKDOWN_FILE_SET.has(name)) {\n result.push({ name, path: filePath, missing: true });\n }\n }\n\n return result;\n}\n"],"mappings":";;;;;YAG8D;AAa9D,MAAM,qBAA0C;CAC9C;CACA;CACA;CACA;CACA;CACA;CACD;AAED,SAAS,gBAAgB,UAAiC;AACxD,KAAI,CAAC,WAAW,SAAS,CACvB,QAAO;AAET,KAAI;AACF,SAAO,iBAAiB,aAAa,UAAU,QAAQ,CAAC;SAClD;AACN,SAAO;;;AAIX,SAAgB,6BAA6B,iBAA+D;AAC1G,KAAI,CAAC,gBACH,QAAO,EAAE;CAEX,MAAM,WAAW,QAAQ,gBAAgB;CACzC,MAAM,UAAU,gBAAgB,SAAS;AACzC,QAAO,YAAY,OACf,CAAC;EAAE,MAAM;EAAuB,MAAM;EAAU;EAAS,SAAS;EAAO,CAAC,GAC1E,EAAE;;;;;;AAOR,SAAgB,0BAA0B,YAA8C;CACtF,MAAM,cAAc,QAAQ,WAAW;CACvC,MAAM,SAAmC,EAAE;AAE3C,MAAK,MAAM,QAAQ,oBAAoB;EACrC,MAAM,WAAW,KAAK,aAAa,KAAK;EAExC,MAAM,UAAU,gBAAgB,SAAS;AACzC,MAAI,YAAY,KACd,QAAO,KAAK;GAAE;GAAM,MAAM;GAAU;GAAS,SAAS;GAAO,CAAC;WACrD,yCAAyC,IAAI,KAAK,CAC3D,QAAO,KAAK;GAAE;GAAM,MAAM;GAAU,SAAS;GAAM,CAAC;;AAIxD,QAAO"}
@@ -1,5 +1,5 @@
1
1
  import type { AgentProfileMarkdownFilename } from '../context/workspace.js';
2
- export type BootstrapFileName = AgentProfileMarkdownFilename;
2
+ export type BootstrapFileName = AgentProfileMarkdownFilename | 'PROFILE.md';
3
3
  export type WorkspaceBootstrapFile = {
4
4
  name: BootstrapFileName;
5
5
  path: string;
@@ -11,7 +11,7 @@ This workspace is the agent's working area. Treat it as user-owned state.
11
11
 
12
12
  ## Startup Context
13
13
 
14
- Use runtime-provided startup context first. It may already include profile files such as `SOUL.md`, `IDENTITY.md`, `USER.md`, `TOOLS.md`, `AGENTS.md`, `HEARTBEAT.md`, and `MEMORY.md`.
14
+ Use runtime-provided startup context first. It may already include the global user profile and profile files such as `SOUL.md`, `IDENTITY.md`, `TOOLS.md`, `AGENTS.md`, `HEARTBEAT.md`, and `MEMORY.md`.
15
15
 
16
16
  Do not manually reread startup files unless:
17
17
 
@@ -8,14 +8,16 @@ export declare const DEFAULT_AGENTS_FILENAME = "AGENTS.md";
8
8
  export declare const DEFAULT_SOUL_FILENAME = "SOUL.md";
9
9
  export declare const DEFAULT_TOOLS_FILENAME = "TOOLS.md";
10
10
  export declare const DEFAULT_IDENTITY_FILENAME = "IDENTITY.md";
11
- export declare const DEFAULT_USER_FILENAME = "USER.md";
12
11
  export declare const DEFAULT_HEARTBEAT_FILENAME = "HEARTBEAT.md";
13
12
  export declare const DEFAULT_MEMORY_FILENAME = "MEMORY.md";
14
- export type AgentProfileMarkdownFilename = typeof DEFAULT_AGENTS_FILENAME | typeof DEFAULT_SOUL_FILENAME | typeof DEFAULT_TOOLS_FILENAME | typeof DEFAULT_IDENTITY_FILENAME | typeof DEFAULT_USER_FILENAME | typeof DEFAULT_HEARTBEAT_FILENAME | typeof DEFAULT_MEMORY_FILENAME;
13
+ export type AgentProfileMarkdownFilename = typeof DEFAULT_AGENTS_FILENAME | typeof DEFAULT_SOUL_FILENAME | typeof DEFAULT_TOOLS_FILENAME | typeof DEFAULT_IDENTITY_FILENAME | typeof DEFAULT_HEARTBEAT_FILENAME | typeof DEFAULT_MEMORY_FILENAME;
15
14
  /**
16
15
  * Order for loading profile Markdown files into the system prompt (persona / user before repo guide, etc.).
17
16
  */
18
17
  export declare const AGENT_PROFILE_MARKDOWN_SYSTEM_FILES: readonly AgentProfileMarkdownFilename[];
18
+ /** Files that should be surfaced when missing. Other profile files are optional overlays. */
19
+ export declare const REQUIRED_AGENT_PROFILE_MARKDOWN_FILES: readonly AgentProfileMarkdownFilename[];
20
+ export declare const REQUIRED_AGENT_PROFILE_MARKDOWN_FILE_SET: ReadonlySet<string>;
19
21
  export interface WorkspaceProfileMarkdownFile {
20
22
  name: AgentProfileMarkdownFilename;
21
23
  path: string;
@@ -16,7 +16,6 @@ const DEFAULT_AGENTS_FILENAME = "AGENTS.md";
16
16
  const DEFAULT_SOUL_FILENAME = "SOUL.md";
17
17
  const DEFAULT_TOOLS_FILENAME = "TOOLS.md";
18
18
  const DEFAULT_IDENTITY_FILENAME = "IDENTITY.md";
19
- const DEFAULT_USER_FILENAME = "USER.md";
20
19
  const DEFAULT_HEARTBEAT_FILENAME = "HEARTBEAT.md";
21
20
  const DEFAULT_MEMORY_FILENAME = "MEMORY.md";
22
21
  /**
@@ -25,12 +24,14 @@ const DEFAULT_MEMORY_FILENAME = "MEMORY.md";
25
24
  const AGENT_PROFILE_MARKDOWN_SYSTEM_FILES = [
26
25
  DEFAULT_SOUL_FILENAME,
27
26
  DEFAULT_IDENTITY_FILENAME,
28
- DEFAULT_USER_FILENAME,
29
27
  DEFAULT_TOOLS_FILENAME,
30
28
  DEFAULT_AGENTS_FILENAME,
31
29
  DEFAULT_HEARTBEAT_FILENAME,
32
30
  DEFAULT_MEMORY_FILENAME
33
31
  ];
32
+ /** Files that should be surfaced when missing. Other profile files are optional overlays. */
33
+ const REQUIRED_AGENT_PROFILE_MARKDOWN_FILES = [DEFAULT_SOUL_FILENAME, DEFAULT_IDENTITY_FILENAME];
34
+ const REQUIRED_AGENT_PROFILE_MARKDOWN_FILE_SET = new Set(REQUIRED_AGENT_PROFILE_MARKDOWN_FILES);
34
35
  /**
35
36
  * Strip YAML front matter from markdown content.
36
37
  */
@@ -118,7 +119,7 @@ function loadProfileMarkdownFiles(profileDir) {
118
119
  err
119
120
  }, "Failed to load profile Markdown file");
120
121
  }
121
- else {
122
+ else if (REQUIRED_AGENT_PROFILE_MARKDOWN_FILE_SET.has(filename)) {
122
123
  const hint = join(profileDir, filename);
123
124
  files.push({
124
125
  name: filename,
@@ -165,6 +166,6 @@ function extractThinkingFromAssistantMessage(message) {
165
166
  return typeof t === "string" ? t : "";
166
167
  }
167
168
  //#endregion
168
- export { AGENT_PROFILE_MARKDOWN_SYSTEM_FILES, DEFAULT_AGENTS_FILENAME, DEFAULT_HEARTBEAT_FILENAME, DEFAULT_IDENTITY_FILENAME, DEFAULT_MEMORY_FILENAME, DEFAULT_SOUL_FILENAME, DEFAULT_TOOLS_FILENAME, DEFAULT_USER_FILENAME, PROFILE_MARKDOWN_MAX_CHARS, extractTextContent, extractThinkingContent, extractThinkingFromAssistantMessage, loadProfileMarkdownFiles, stripFrontMatter, toWorkspaceProfileMarkdownFile, truncateProfileMarkdownContent };
169
+ export { AGENT_PROFILE_MARKDOWN_SYSTEM_FILES, DEFAULT_AGENTS_FILENAME, DEFAULT_HEARTBEAT_FILENAME, DEFAULT_IDENTITY_FILENAME, DEFAULT_MEMORY_FILENAME, DEFAULT_SOUL_FILENAME, DEFAULT_TOOLS_FILENAME, PROFILE_MARKDOWN_MAX_CHARS, REQUIRED_AGENT_PROFILE_MARKDOWN_FILES, REQUIRED_AGENT_PROFILE_MARKDOWN_FILE_SET, extractTextContent, extractThinkingContent, extractThinkingFromAssistantMessage, loadProfileMarkdownFiles, stripFrontMatter, toWorkspaceProfileMarkdownFile, truncateProfileMarkdownContent };
169
170
 
170
171
  //# sourceMappingURL=workspace.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.js","names":[],"sources":["../../../../src/agent/context/workspace.ts"],"sourcesContent":["/**\n * Agent profile Markdown (filenames, load order, system prompt assembly) and\n * small helpers for message content extraction.\n *\n * Canonical on-disk root: `agents/<agentId>/profile/`.\n */\n\nimport { readFileSync, existsSync } from 'fs';\nimport { join, resolve } from 'path';\nimport { createLogger } from '../../utils/logger.js';\nimport { parseFrontmatter } from '../../markdown/frontmatter.js';\n\nconst log = createLogger('Workspace');\n\n// =============================================================================\n// Profile Markdown filenames & types (SOUL.md, IDENTITY.md, … under `profile/`)\n// =============================================================================\n\nexport const DEFAULT_AGENTS_FILENAME = 'AGENTS.md';\nexport const DEFAULT_SOUL_FILENAME = 'SOUL.md';\nexport const DEFAULT_TOOLS_FILENAME = 'TOOLS.md';\nexport const DEFAULT_IDENTITY_FILENAME = 'IDENTITY.md';\nexport const DEFAULT_USER_FILENAME = 'USER.md';\nexport const DEFAULT_HEARTBEAT_FILENAME = 'HEARTBEAT.md';\nexport const DEFAULT_MEMORY_FILENAME = 'MEMORY.md';\n\nexport type AgentProfileMarkdownFilename =\n | typeof DEFAULT_AGENTS_FILENAME\n | typeof DEFAULT_SOUL_FILENAME\n | typeof DEFAULT_TOOLS_FILENAME\n | typeof DEFAULT_IDENTITY_FILENAME\n | typeof DEFAULT_USER_FILENAME\n | typeof DEFAULT_HEARTBEAT_FILENAME\n | typeof DEFAULT_MEMORY_FILENAME;\n\n/**\n * Order for loading profile Markdown files into the system prompt (persona / user before repo guide, etc.).\n */\nexport const AGENT_PROFILE_MARKDOWN_SYSTEM_FILES: readonly AgentProfileMarkdownFilename[] = [\n DEFAULT_SOUL_FILENAME,\n DEFAULT_IDENTITY_FILENAME,\n DEFAULT_USER_FILENAME,\n DEFAULT_TOOLS_FILENAME,\n DEFAULT_AGENTS_FILENAME,\n DEFAULT_HEARTBEAT_FILENAME,\n DEFAULT_MEMORY_FILENAME,\n];\n\nexport interface WorkspaceProfileMarkdownFile {\n name: AgentProfileMarkdownFilename;\n path: string;\n content?: string;\n missing: boolean;\n}\n\n/**\n * Strip YAML front matter from markdown content.\n */\nexport function stripFrontMatter(content: string): string {\n return parseFrontmatter(content).content;\n}\n\n// =============================================================================\n// Load & truncate profile Markdown for system prompt\n// =============================================================================\n\n/** Maximum characters to inject from workspace files into system prompt */\nexport const PROFILE_MARKDOWN_MAX_CHARS = 20_000;\n\n/** Profile Markdown truncation: fraction of `maxChars` kept from the start of the file */\nconst HEAD_TRUNCATION_RATIO = 0.7;\n/** Profile Markdown truncation: fraction of `maxChars` kept from the end of the file */\nconst TAIL_TRUNCATION_RATIO = 0.2;\n\nexport interface TruncateResult {\n content: string;\n truncated: boolean;\n originalLength: number;\n}\n\n/**\n * Truncate workspace file content to prevent token overflow.\n * Keeps head (HEAD_TRUNCATION_RATIO, 70%) and tail (TAIL_TRUNCATION_RATIO, 20%) with a\n * truncation marker in between; the remainder is reserved for the marker.\n */\nexport function truncateProfileMarkdownContent(content: string, maxChars: number): TruncateResult {\n const trimmed = content.trimEnd();\n if (trimmed.length <= maxChars) {\n return {\n content: trimmed,\n truncated: false,\n originalLength: trimmed.length,\n };\n }\n\n const headChars = Math.floor(maxChars * HEAD_TRUNCATION_RATIO);\n const tailChars = Math.floor(maxChars * TAIL_TRUNCATION_RATIO);\n const head = trimmed.slice(0, headChars);\n const tail = trimmed.slice(-tailChars);\n\n const marker = [\n '',\n '[...content truncated, read the full file for complete content...]',\n `...(truncated: kept ${headChars}+${tailChars} chars of ${trimmed.length})...`,\n '',\n ].join('\\n');\n\n return {\n content: head + marker + tail,\n truncated: true,\n originalLength: trimmed.length,\n };\n}\n\nexport interface ProfileMarkdownFile {\n name: AgentProfileMarkdownFilename;\n path?: string;\n content: string;\n missing?: boolean;\n}\n\n/**\n * Convert {@link ProfileMarkdownFile} to {@link WorkspaceProfileMarkdownFile} (adds required path field).\n * @param pathFallbackDir — used when `file.path` is absent (e.g. canonical `profile/` root).\n */\nexport function toWorkspaceProfileMarkdownFile(file: ProfileMarkdownFile, pathFallbackDir: string): {\n name: AgentProfileMarkdownFilename;\n path: string;\n content?: string;\n missing: boolean;\n} {\n return {\n name: file.name,\n path: file.path || join(pathFallbackDir, file.name),\n content: file.missing ? undefined : file.content,\n missing: file.missing ?? false,\n };\n}\n\n/**\n * Load profile Markdown from `profileDir` (`agents/<agentId>/profile/`).\n */\nexport function loadProfileMarkdownFiles(profileDir: string): ProfileMarkdownFile[] {\n const files: ProfileMarkdownFile[] = [];\n let loadedCount = 0;\n let missingCount = 0;\n\n for (const filename of AGENT_PROFILE_MARKDOWN_SYSTEM_FILES) {\n const filePath = join(profileDir, filename);\n\n if (existsSync(filePath)) {\n try {\n let content = readFileSync(filePath, 'utf-8');\n\n content = stripFrontMatter(content);\n\n const result = truncateProfileMarkdownContent(content, PROFILE_MARKDOWN_MAX_CHARS);\n\n if (result.content) {\n files.push({\n name: filename,\n content: result.content,\n path: resolve(filePath),\n });\n loadedCount++;\n\n if (result.truncated) {\n log.warn(\n {\n file: filename,\n originalLength: result.originalLength,\n keptLength: result.content.length,\n },\n 'Profile Markdown truncated in system prompt (too long)',\n );\n } else {\n log.debug({ file: filename, path: filePath }, 'Profile Markdown file loaded');\n }\n }\n } catch (err) {\n log.warn({ file: filename, err }, 'Failed to load profile Markdown file');\n }\n } else {\n const hint = join(profileDir, filename);\n files.push({\n name: filename,\n content: `[MISSING] Create this file at: ${hint}`,\n missing: true,\n path: resolve(hint),\n });\n missingCount++;\n log.debug({ file: filename }, 'Profile Markdown file missing');\n }\n }\n\n log.debug({ loaded: loadedCount, missing: missingCount, profileDir }, 'Profile Markdown scan done');\n\n return files;\n}\n\n// =============================================================================\n// Message content helpers\n// =============================================================================\n\n/**\n * Extract text content from agent message content array\n */\nexport function extractTextContent(\n content: Array<{ type: string; text?: string }>\n): string {\n return content\n .filter((c) => c.type === 'text')\n .map((c) => c.text || '')\n .join('');\n}\n\nconst THINKING_BLOCK_TYPES = new Set(['thinking', 'thinking_delta', 'redacted_thinking']);\n\n/**\n * Concatenate thinking / reasoning content blocks from assistant message.content.\n * pi-ai may use `thinking` or `text` on `type: \"thinking\"` blocks.\n */\nexport function extractThinkingContent(\n content: Array<{ type: string; thinking?: string; text?: string }> | undefined\n): string {\n if (!Array.isArray(content)) return '';\n return content\n .filter((c) => THINKING_BLOCK_TYPES.has(c.type))\n .map((c) => (typeof c.thinking === 'string' ? c.thinking : c.text) || '')\n .join('');\n}\n\n/**\n * Extended thinking on the assistant message (e.g. Anthropic reasoning / pi-ai reasoning_details).\n */\nexport function extractThinkingFromAssistantMessage(\n message: { role?: string; reasoning_details?: { thinking?: string } } | undefined\n): string {\n if (!message || message.role !== 'assistant') return '';\n const t = message.reasoning_details?.thinking;\n return typeof t === 'string' ? t : '';\n}\n"],"mappings":";;;;;;;;;;;;aASqD;AAGrD,MAAM,MAAM,aAAa,YAAY;AAMrC,MAAa,0BAA0B;AACvC,MAAa,wBAAwB;AACrC,MAAa,yBAAyB;AACtC,MAAa,4BAA4B;AACzC,MAAa,wBAAwB;AACrC,MAAa,6BAA6B;AAC1C,MAAa,0BAA0B;;;;AAcvC,MAAa,sCAA+E;CAC1F;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;AAYD,SAAgB,iBAAiB,SAAyB;AACxD,QAAO,iBAAiB,QAAQ,CAAC;;;AAQnC,MAAa,6BAA6B;;AAG1C,MAAM,wBAAwB;;AAE9B,MAAM,wBAAwB;;;;;;AAa9B,SAAgB,+BAA+B,SAAiB,UAAkC;CAChG,MAAM,UAAU,QAAQ,SAAS;AACjC,KAAI,QAAQ,UAAU,SACpB,QAAO;EACL,SAAS;EACT,WAAW;EACX,gBAAgB,QAAQ;EACzB;CAGH,MAAM,YAAY,KAAK,MAAM,WAAW,sBAAsB;CAC9D,MAAM,YAAY,KAAK,MAAM,WAAW,sBAAsB;CAC9D,MAAM,OAAO,QAAQ,MAAM,GAAG,UAAU;CACxC,MAAM,OAAO,QAAQ,MAAM,CAAC,UAAU;AAStC,QAAO;EACL,SAAS,OARI;GACb;GACA;GACA,uBAAuB,UAAU,GAAG,UAAU,YAAY,QAAQ,OAAO;GACzE;GACD,CAAC,KAAK,KAGiB,GAAG;EACzB,WAAW;EACX,gBAAgB,QAAQ;EACzB;;;;;;AAcH,SAAgB,+BAA+B,MAA2B,iBAKxE;AACA,QAAO;EACL,MAAM,KAAK;EACX,MAAM,KAAK,QAAQ,KAAK,iBAAiB,KAAK,KAAK;EACnD,SAAS,KAAK,UAAU,KAAA,IAAY,KAAK;EACzC,SAAS,KAAK,WAAW;EAC1B;;;;;AAMH,SAAgB,yBAAyB,YAA2C;CAClF,MAAM,QAA+B,EAAE;CACvC,IAAI,cAAc;CAClB,IAAI,eAAe;AAEnB,MAAK,MAAM,YAAY,qCAAqC;EAC1D,MAAM,WAAW,KAAK,YAAY,SAAS;AAE3C,MAAI,WAAW,SAAS,CACtB,KAAI;GACF,IAAI,UAAU,aAAa,UAAU,QAAQ;AAE7C,aAAU,iBAAiB,QAAQ;GAEnC,MAAM,SAAS,+BAA+B,SAAS,2BAA2B;AAElF,OAAI,OAAO,SAAS;AAClB,UAAM,KAAK;KACT,MAAM;KACN,SAAS,OAAO;KAChB,MAAM,QAAQ,SAAS;KACxB,CAAC;AACF;AAEA,QAAI,OAAO,UACT,KAAI,KACF;KACE,MAAM;KACN,gBAAgB,OAAO;KACvB,YAAY,OAAO,QAAQ;KAC5B,EACD,yDACD;QAED,KAAI,MAAM;KAAE,MAAM;KAAU,MAAM;KAAU,EAAE,+BAA+B;;WAG1E,KAAK;AACZ,OAAI,KAAK;IAAE,MAAM;IAAU;IAAK,EAAE,uCAAuC;;OAEtE;GACL,MAAM,OAAO,KAAK,YAAY,SAAS;AACvC,SAAM,KAAK;IACT,MAAM;IACN,SAAS,kCAAkC;IAC3C,SAAS;IACT,MAAM,QAAQ,KAAK;IACpB,CAAC;AACF;AACA,OAAI,MAAM,EAAE,MAAM,UAAU,EAAE,gCAAgC;;;AAIlE,KAAI,MAAM;EAAE,QAAQ;EAAa,SAAS;EAAc;EAAY,EAAE,6BAA6B;AAEnG,QAAO;;;;;AAUT,SAAgB,mBACd,SACQ;AACR,QAAO,QACJ,QAAQ,MAAM,EAAE,SAAS,OAAO,CAChC,KAAK,MAAM,EAAE,QAAQ,GAAG,CACxB,KAAK,GAAG;;AAGb,MAAM,uBAAuB,IAAI,IAAI;CAAC;CAAY;CAAkB;CAAoB,CAAC;;;;;AAMzF,SAAgB,uBACd,SACQ;AACR,KAAI,CAAC,MAAM,QAAQ,QAAQ,CAAE,QAAO;AACpC,QAAO,QACJ,QAAQ,MAAM,qBAAqB,IAAI,EAAE,KAAK,CAAC,CAC/C,KAAK,OAAO,OAAO,EAAE,aAAa,WAAW,EAAE,WAAW,EAAE,SAAS,GAAG,CACxE,KAAK,GAAG;;;;;AAMb,SAAgB,oCACd,SACQ;AACR,KAAI,CAAC,WAAW,QAAQ,SAAS,YAAa,QAAO;CACrD,MAAM,IAAI,QAAQ,mBAAmB;AACrC,QAAO,OAAO,MAAM,WAAW,IAAI"}
1
+ {"version":3,"file":"workspace.js","names":[],"sources":["../../../../src/agent/context/workspace.ts"],"sourcesContent":["/**\n * Agent profile Markdown (filenames, load order, system prompt assembly) and\n * small helpers for message content extraction.\n *\n * Canonical on-disk root: `agents/<agentId>/profile/`.\n */\n\nimport { readFileSync, existsSync } from 'fs';\nimport { join, resolve } from 'path';\nimport { createLogger } from '../../utils/logger.js';\nimport { parseFrontmatter } from '../../markdown/frontmatter.js';\n\nconst log = createLogger('Workspace');\n\n// =============================================================================\n// Profile Markdown filenames & types (SOUL.md, IDENTITY.md, … under `profile/`)\n// =============================================================================\n\nexport const DEFAULT_AGENTS_FILENAME = 'AGENTS.md';\nexport const DEFAULT_SOUL_FILENAME = 'SOUL.md';\nexport const DEFAULT_TOOLS_FILENAME = 'TOOLS.md';\nexport const DEFAULT_IDENTITY_FILENAME = 'IDENTITY.md';\nexport const DEFAULT_HEARTBEAT_FILENAME = 'HEARTBEAT.md';\nexport const DEFAULT_MEMORY_FILENAME = 'MEMORY.md';\n\nexport type AgentProfileMarkdownFilename =\n | typeof DEFAULT_AGENTS_FILENAME\n | typeof DEFAULT_SOUL_FILENAME\n | typeof DEFAULT_TOOLS_FILENAME\n | typeof DEFAULT_IDENTITY_FILENAME\n | typeof DEFAULT_HEARTBEAT_FILENAME\n | typeof DEFAULT_MEMORY_FILENAME;\n\n/**\n * Order for loading profile Markdown files into the system prompt (persona / user before repo guide, etc.).\n */\nexport const AGENT_PROFILE_MARKDOWN_SYSTEM_FILES: readonly AgentProfileMarkdownFilename[] = [\n DEFAULT_SOUL_FILENAME,\n DEFAULT_IDENTITY_FILENAME,\n DEFAULT_TOOLS_FILENAME,\n DEFAULT_AGENTS_FILENAME,\n DEFAULT_HEARTBEAT_FILENAME,\n DEFAULT_MEMORY_FILENAME,\n];\n\n/** Files that should be surfaced when missing. Other profile files are optional overlays. */\nexport const REQUIRED_AGENT_PROFILE_MARKDOWN_FILES: readonly AgentProfileMarkdownFilename[] = [\n DEFAULT_SOUL_FILENAME,\n DEFAULT_IDENTITY_FILENAME,\n];\n\nexport const REQUIRED_AGENT_PROFILE_MARKDOWN_FILE_SET: ReadonlySet<string> = new Set(\n REQUIRED_AGENT_PROFILE_MARKDOWN_FILES,\n);\n\nexport interface WorkspaceProfileMarkdownFile {\n name: AgentProfileMarkdownFilename;\n path: string;\n content?: string;\n missing: boolean;\n}\n\n/**\n * Strip YAML front matter from markdown content.\n */\nexport function stripFrontMatter(content: string): string {\n return parseFrontmatter(content).content;\n}\n\n// =============================================================================\n// Load & truncate profile Markdown for system prompt\n// =============================================================================\n\n/** Maximum characters to inject from workspace files into system prompt */\nexport const PROFILE_MARKDOWN_MAX_CHARS = 20_000;\n\n/** Profile Markdown truncation: fraction of `maxChars` kept from the start of the file */\nconst HEAD_TRUNCATION_RATIO = 0.7;\n/** Profile Markdown truncation: fraction of `maxChars` kept from the end of the file */\nconst TAIL_TRUNCATION_RATIO = 0.2;\n\nexport interface TruncateResult {\n content: string;\n truncated: boolean;\n originalLength: number;\n}\n\n/**\n * Truncate workspace file content to prevent token overflow.\n * Keeps head (HEAD_TRUNCATION_RATIO, 70%) and tail (TAIL_TRUNCATION_RATIO, 20%) with a\n * truncation marker in between; the remainder is reserved for the marker.\n */\nexport function truncateProfileMarkdownContent(content: string, maxChars: number): TruncateResult {\n const trimmed = content.trimEnd();\n if (trimmed.length <= maxChars) {\n return {\n content: trimmed,\n truncated: false,\n originalLength: trimmed.length,\n };\n }\n\n const headChars = Math.floor(maxChars * HEAD_TRUNCATION_RATIO);\n const tailChars = Math.floor(maxChars * TAIL_TRUNCATION_RATIO);\n const head = trimmed.slice(0, headChars);\n const tail = trimmed.slice(-tailChars);\n\n const marker = [\n '',\n '[...content truncated, read the full file for complete content...]',\n `...(truncated: kept ${headChars}+${tailChars} chars of ${trimmed.length})...`,\n '',\n ].join('\\n');\n\n return {\n content: head + marker + tail,\n truncated: true,\n originalLength: trimmed.length,\n };\n}\n\nexport interface ProfileMarkdownFile {\n name: AgentProfileMarkdownFilename;\n path?: string;\n content: string;\n missing?: boolean;\n}\n\n/**\n * Convert {@link ProfileMarkdownFile} to {@link WorkspaceProfileMarkdownFile} (adds required path field).\n * @param pathFallbackDir — used when `file.path` is absent (e.g. canonical `profile/` root).\n */\nexport function toWorkspaceProfileMarkdownFile(file: ProfileMarkdownFile, pathFallbackDir: string): {\n name: AgentProfileMarkdownFilename;\n path: string;\n content?: string;\n missing: boolean;\n} {\n return {\n name: file.name,\n path: file.path || join(pathFallbackDir, file.name),\n content: file.missing ? undefined : file.content,\n missing: file.missing ?? false,\n };\n}\n\n/**\n * Load profile Markdown from `profileDir` (`agents/<agentId>/profile/`).\n */\nexport function loadProfileMarkdownFiles(profileDir: string): ProfileMarkdownFile[] {\n const files: ProfileMarkdownFile[] = [];\n let loadedCount = 0;\n let missingCount = 0;\n\n for (const filename of AGENT_PROFILE_MARKDOWN_SYSTEM_FILES) {\n const filePath = join(profileDir, filename);\n\n if (existsSync(filePath)) {\n try {\n let content = readFileSync(filePath, 'utf-8');\n\n content = stripFrontMatter(content);\n\n const result = truncateProfileMarkdownContent(content, PROFILE_MARKDOWN_MAX_CHARS);\n\n if (result.content) {\n files.push({\n name: filename,\n content: result.content,\n path: resolve(filePath),\n });\n loadedCount++;\n\n if (result.truncated) {\n log.warn(\n {\n file: filename,\n originalLength: result.originalLength,\n keptLength: result.content.length,\n },\n 'Profile Markdown truncated in system prompt (too long)',\n );\n } else {\n log.debug({ file: filename, path: filePath }, 'Profile Markdown file loaded');\n }\n }\n } catch (err) {\n log.warn({ file: filename, err }, 'Failed to load profile Markdown file');\n }\n } else if (REQUIRED_AGENT_PROFILE_MARKDOWN_FILE_SET.has(filename)) {\n const hint = join(profileDir, filename);\n files.push({\n name: filename,\n content: `[MISSING] Create this file at: ${hint}`,\n missing: true,\n path: resolve(hint),\n });\n missingCount++;\n log.debug({ file: filename }, 'Profile Markdown file missing');\n }\n }\n\n log.debug({ loaded: loadedCount, missing: missingCount, profileDir }, 'Profile Markdown scan done');\n\n return files;\n}\n\n// =============================================================================\n// Message content helpers\n// =============================================================================\n\n/**\n * Extract text content from agent message content array\n */\nexport function extractTextContent(\n content: Array<{ type: string; text?: string }>\n): string {\n return content\n .filter((c) => c.type === 'text')\n .map((c) => c.text || '')\n .join('');\n}\n\nconst THINKING_BLOCK_TYPES = new Set(['thinking', 'thinking_delta', 'redacted_thinking']);\n\n/**\n * Concatenate thinking / reasoning content blocks from assistant message.content.\n * pi-ai may use `thinking` or `text` on `type: \"thinking\"` blocks.\n */\nexport function extractThinkingContent(\n content: Array<{ type: string; thinking?: string; text?: string }> | undefined\n): string {\n if (!Array.isArray(content)) return '';\n return content\n .filter((c) => THINKING_BLOCK_TYPES.has(c.type))\n .map((c) => (typeof c.thinking === 'string' ? c.thinking : c.text) || '')\n .join('');\n}\n\n/**\n * Extended thinking on the assistant message (e.g. Anthropic reasoning / pi-ai reasoning_details).\n */\nexport function extractThinkingFromAssistantMessage(\n message: { role?: string; reasoning_details?: { thinking?: string } } | undefined\n): string {\n if (!message || message.role !== 'assistant') return '';\n const t = message.reasoning_details?.thinking;\n return typeof t === 'string' ? t : '';\n}\n"],"mappings":";;;;;;;;;;;;aASqD;AAGrD,MAAM,MAAM,aAAa,YAAY;AAMrC,MAAa,0BAA0B;AACvC,MAAa,wBAAwB;AACrC,MAAa,yBAAyB;AACtC,MAAa,4BAA4B;AACzC,MAAa,6BAA6B;AAC1C,MAAa,0BAA0B;;;;AAavC,MAAa,sCAA+E;CAC1F;CACA;CACA;CACA;CACA;CACA;CACD;;AAGD,MAAa,wCAAiF,CAC5F,uBACA,0BACD;AAED,MAAa,2CAAgE,IAAI,IAC/E,sCACD;;;;AAYD,SAAgB,iBAAiB,SAAyB;AACxD,QAAO,iBAAiB,QAAQ,CAAC;;;AAQnC,MAAa,6BAA6B;;AAG1C,MAAM,wBAAwB;;AAE9B,MAAM,wBAAwB;;;;;;AAa9B,SAAgB,+BAA+B,SAAiB,UAAkC;CAChG,MAAM,UAAU,QAAQ,SAAS;AACjC,KAAI,QAAQ,UAAU,SACpB,QAAO;EACL,SAAS;EACT,WAAW;EACX,gBAAgB,QAAQ;EACzB;CAGH,MAAM,YAAY,KAAK,MAAM,WAAW,sBAAsB;CAC9D,MAAM,YAAY,KAAK,MAAM,WAAW,sBAAsB;CAC9D,MAAM,OAAO,QAAQ,MAAM,GAAG,UAAU;CACxC,MAAM,OAAO,QAAQ,MAAM,CAAC,UAAU;AAStC,QAAO;EACL,SAAS,OARI;GACb;GACA;GACA,uBAAuB,UAAU,GAAG,UAAU,YAAY,QAAQ,OAAO;GACzE;GACD,CAAC,KAAK,KAGiB,GAAG;EACzB,WAAW;EACX,gBAAgB,QAAQ;EACzB;;;;;;AAcH,SAAgB,+BAA+B,MAA2B,iBAKxE;AACA,QAAO;EACL,MAAM,KAAK;EACX,MAAM,KAAK,QAAQ,KAAK,iBAAiB,KAAK,KAAK;EACnD,SAAS,KAAK,UAAU,KAAA,IAAY,KAAK;EACzC,SAAS,KAAK,WAAW;EAC1B;;;;;AAMH,SAAgB,yBAAyB,YAA2C;CAClF,MAAM,QAA+B,EAAE;CACvC,IAAI,cAAc;CAClB,IAAI,eAAe;AAEnB,MAAK,MAAM,YAAY,qCAAqC;EAC1D,MAAM,WAAW,KAAK,YAAY,SAAS;AAE3C,MAAI,WAAW,SAAS,CACtB,KAAI;GACF,IAAI,UAAU,aAAa,UAAU,QAAQ;AAE7C,aAAU,iBAAiB,QAAQ;GAEnC,MAAM,SAAS,+BAA+B,SAAS,2BAA2B;AAElF,OAAI,OAAO,SAAS;AAClB,UAAM,KAAK;KACT,MAAM;KACN,SAAS,OAAO;KAChB,MAAM,QAAQ,SAAS;KACxB,CAAC;AACF;AAEA,QAAI,OAAO,UACT,KAAI,KACF;KACE,MAAM;KACN,gBAAgB,OAAO;KACvB,YAAY,OAAO,QAAQ;KAC5B,EACD,yDACD;QAED,KAAI,MAAM;KAAE,MAAM;KAAU,MAAM;KAAU,EAAE,+BAA+B;;WAG1E,KAAK;AACZ,OAAI,KAAK;IAAE,MAAM;IAAU;IAAK,EAAE,uCAAuC;;WAElE,yCAAyC,IAAI,SAAS,EAAE;GACjE,MAAM,OAAO,KAAK,YAAY,SAAS;AACvC,SAAM,KAAK;IACT,MAAM;IACN,SAAS,kCAAkC;IAC3C,SAAS;IACT,MAAM,QAAQ,KAAK;IACpB,CAAC;AACF;AACA,OAAI,MAAM,EAAE,MAAM,UAAU,EAAE,gCAAgC;;;AAIlE,KAAI,MAAM;EAAE,QAAQ;EAAa,SAAS;EAAc;EAAY,EAAE,6BAA6B;AAEnG,QAAO;;;;;AAUT,SAAgB,mBACd,SACQ;AACR,QAAO,QACJ,QAAQ,MAAM,EAAE,SAAS,OAAO,CAChC,KAAK,MAAM,EAAE,QAAQ,GAAG,CACxB,KAAK,GAAG;;AAGb,MAAM,uBAAuB,IAAI,IAAI;CAAC;CAAY;CAAkB;CAAoB,CAAC;;;;;AAMzF,SAAgB,uBACd,SACQ;AACR,KAAI,CAAC,MAAM,QAAQ,QAAQ,CAAE,QAAO;AACpC,QAAO,QACJ,QAAQ,MAAM,qBAAqB,IAAI,EAAE,KAAK,CAAC,CAC/C,KAAK,OAAO,OAAO,EAAE,aAAa,WAAW,EAAE,WAAW,EAAE,SAAS,GAAG,CACxE,KAAK,GAAG;;;;;AAMb,SAAgB,oCACd,SACQ;AACR,KAAI,CAAC,WAAW,QAAQ,SAAS,YAAa,QAAO;CACrD,MAAM,IAAI,QAAQ,mBAAmB;AACrC,QAAO,OAAO,MAAM,WAAW,IAAI"}
@@ -1,3 +1,4 @@
1
+ import { resolveEffectiveAgentManifestForSession } from "../../config/agent-profile.js";
1
2
  import { createLogger } from "../../utils/logger/index.js";
2
3
  import { init_logger } from "../../utils/logger.js";
3
4
  import { resolveAgentTurnTimeoutMs } from "../orchestration/run-agent-turn-with-timeout.js";
@@ -74,7 +75,9 @@ async function runEmbeddedTurnForSession(params) {
74
75
  }
75
76
  async function maybeAutoCompactBeforeTurn(opts) {
76
77
  const { sessionKey, sessionStore, agentManager, model, config, onEvent } = opts;
77
- if (((config?.agents)?.defaults?.compaction)?.enabled === false) return;
78
+ if (config) try {
79
+ if (resolveEffectiveAgentManifestForSession(config, sessionKey).memory.retention?.compaction === false) return;
80
+ } catch {}
78
81
  const contextWindow = model.contextWindow ?? 128e3;
79
82
  const messages = await sessionStore.load(sessionKey);
80
83
  const prep = sessionStore.prepareCompaction(sessionKey, messages, contextWindow);
@@ -1 +1 @@
1
- {"version":3,"file":"run-for-session.js","names":[],"sources":["../../../../src/agent/embedded/run-for-session.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport type { AgentMessage, ThinkingLevel } from '@earendil-works/pi-agent-core';\n\nimport type { Config } from '../../config/schema.js';\nimport type { AgentInstanceGateway } from '../agent-instance-gateway.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { SessionStore } from '../../session/store.js';\nimport { resolveAgentTurnTimeoutMs } from '../orchestration/run-agent-turn-with-timeout.js';\nimport { runXopcEmbeddedTurn } from './run-turn.js';\nimport type { EmbeddedStreamEvent, RunXopcEmbeddedTurnParams, RunXopcEmbeddedTurnResult } from './types.js';\nimport { applyStartupContextToUserMessage } from '../reply/apply-turn-user-enrichment.js';\nimport {\n mergeTurnTools,\n resolveEmbeddedMcpToolsForTurn,\n} from '../mcp/resolve-embedded-mcp-tools.js';\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('EmbeddedTurnForSession');\n\nexport type RunEmbeddedForSessionParams = {\n sessionKey: string;\n runId?: string;\n userMessage: AgentMessage;\n llmImages?: import('@earendil-works/pi-ai').ImageContent[];\n sessionStore: SessionStore;\n agentManager: AgentInstanceGateway;\n modelManager: ModelManager;\n thinkingOverride?: string | null;\n abortSignal?: AbortSignal;\n onEvent?: (event: EmbeddedStreamEvent) => void;\n getConfig?: () => Config | undefined;\n beforeTurn?: () => void | Promise<void>;\n afterTurn?: (userPlain: string) => void | Promise<void>;\n startupAction?: 'new' | 'reset';\n forceStartupContext?: boolean;\n applyStartupContext?: boolean;\n /** When true, dispose session MCP runtime after this turn completes. */\n cleanupBundleMcpOnRunEnd?: boolean;\n};\n\nexport async function runEmbeddedTurnForSession(\n params: RunEmbeddedForSessionParams,\n): Promise<RunXopcEmbeddedTurnResult> {\n const { sessionKey, agentManager, modelManager, sessionStore, userMessage } = params;\n const runId = params.runId ?? crypto.randomUUID();\n\n await params.beforeTurn?.();\n\n const agent = (agentManager as any).getOrCreateAgent(sessionKey) as {\n state: {\n tools: RunXopcEmbeddedTurnParams['tools'];\n systemPrompt?: string;\n thinkingLevel?: ThinkingLevel;\n };\n };\n const mm = modelManager as any;\n await mm.applyModelForSession(agent, sessionKey);\n\n const modelRef = String(mm.getModelForSession(sessionKey));\n agentManager.setModelForSession(sessionKey, modelRef);\n const model = mm.getResolvedModelForSession(sessionKey) as RunXopcEmbeddedTurnParams['model'];\n const tools = agent.state.tools;\n const systemPrompt = agent.state.systemPrompt ?? '';\n const thinkingLevel = (params.thinkingOverride as ThinkingLevel | undefined) ?? agent.state.thinkingLevel;\n const workspaceDir = agentManager.getResolvedWorkspaceForSession(sessionKey);\n const config = params.getConfig?.();\n\n // --- Pre-turn automatic compaction ---\n await maybeAutoCompactBeforeTurn({\n sessionKey,\n sessionStore,\n agentManager,\n model,\n config,\n onEvent: params.onEvent,\n });\n\n let userMessageForTurn = userMessage;\n if (params.applyStartupContext !== false) {\n userMessageForTurn = await applyStartupContextToUserMessage({\n userMessage,\n sessionKey,\n workspaceDir,\n cfg: config,\n sessionStore,\n startupAction: params.startupAction,\n force: params.forceStartupContext,\n });\n }\n\n const result = await (async () => {\n const mcpResolved = await resolveEmbeddedMcpToolsForTurn({\n sessionKey,\n workspaceDir,\n cfg: config,\n baseTools: tools,\n cleanupOnTurnEnd: params.cleanupBundleMcpOnRunEnd === true,\n });\n const turnTools = mergeTurnTools(tools, mcpResolved.tools);\n try {\n return await runXopcEmbeddedTurn({\n sessionKey,\n runId,\n userMessage: userMessageForTurn,\n images: params.llmImages,\n model,\n modelRef,\n tools: turnTools,\n systemPrompt,\n thinkingLevel,\n workspaceDir,\n sessionStore,\n timeoutMs: resolveAgentTurnTimeoutMs(config),\n abortSignal: params.abortSignal,\n onEvent: params.onEvent,\n });\n } finally {\n await mcpResolved.dispose().catch(() => {});\n }\n })();\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Pre-turn automatic compaction\n// ---------------------------------------------------------------------------\n\nasync function maybeAutoCompactBeforeTurn(opts: {\n sessionKey: string;\n sessionStore: SessionStore;\n agentManager: AgentInstanceGateway;\n model: RunXopcEmbeddedTurnParams['model'];\n config: Config | undefined;\n onEvent?: (event: EmbeddedStreamEvent) => void;\n}): Promise<void> {\n const { sessionKey, sessionStore, agentManager, model, config, onEvent } = opts;\n const compaction = (config?.agents as unknown as {\n defaults?: { compaction?: { enabled?: boolean } };\n } | undefined)?.defaults?.compaction;\n if (compaction?.enabled === false) {\n return;\n }\n\n const contextWindow = (model as { contextWindow?: number }).contextWindow ?? 128_000;\n const messages = await sessionStore.load(sessionKey);\n const prep = sessionStore.prepareCompaction(sessionKey, messages, contextWindow);\n\n if (!prep.needsCompaction) {\n return;\n }\n\n log.info(\n { sessionKey, reason: prep.stats?.reason, usagePercent: prep.stats?.usagePercent, contextWindow },\n 'Pre-turn auto-compaction triggered',\n );\n\n onEvent?.({\n type: 'compaction',\n status: 'started',\n tokensBefore: prep.stats?.usagePercent != null\n ? Math.round((prep.stats.usagePercent as number) * contextWindow)\n : undefined,\n });\n\n try {\n const result = await sessionStore.compact(sessionKey, messages, contextWindow);\n\n if (result.compacted) {\n // Evict the cached agent so the next turn reloads from the compacted transcript\n agentManager.removeAgent(sessionKey);\n\n log.info(\n { sessionKey, tokensBefore: result.tokensBefore, tokensAfter: result.tokensAfter },\n 'Pre-turn auto-compaction completed',\n );\n\n onEvent?.({\n type: 'compaction',\n status: 'completed',\n tokensBefore: result.tokensBefore,\n tokensAfter: result.tokensAfter,\n summary: result.summary.length > 200 ? `${result.summary.slice(0, 200)}…` : result.summary,\n });\n } else {\n onEvent?.({ type: 'compaction', status: 'skipped' });\n }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.warn({ err, sessionKey }, `Pre-turn auto-compaction failed: ${errorMessage}`);\n // Non-fatal: let the turn proceed even if compaction fails\n onEvent?.({ type: 'compaction', status: 'skipped' });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"run-for-session.js","names":[],"sources":["../../../../src/agent/embedded/run-for-session.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport type { AgentMessage, ThinkingLevel } from '@earendil-works/pi-agent-core';\n\nimport type { Config } from '../../config/schema.js';\nimport type { AgentInstanceGateway } from '../agent-instance-gateway.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { SessionStore } from '../../session/store.js';\nimport { resolveAgentTurnTimeoutMs } from '../orchestration/run-agent-turn-with-timeout.js';\nimport { runXopcEmbeddedTurn } from './run-turn.js';\nimport type { EmbeddedStreamEvent, RunXopcEmbeddedTurnParams, RunXopcEmbeddedTurnResult } from './types.js';\nimport { applyStartupContextToUserMessage } from '../reply/apply-turn-user-enrichment.js';\nimport { resolveEffectiveAgentManifestForSession } from '../../config/agent-profile.js';\nimport {\n mergeTurnTools,\n resolveEmbeddedMcpToolsForTurn,\n} from '../mcp/resolve-embedded-mcp-tools.js';\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('EmbeddedTurnForSession');\n\nexport type RunEmbeddedForSessionParams = {\n sessionKey: string;\n runId?: string;\n userMessage: AgentMessage;\n llmImages?: import('@earendil-works/pi-ai').ImageContent[];\n sessionStore: SessionStore;\n agentManager: AgentInstanceGateway;\n modelManager: ModelManager;\n thinkingOverride?: string | null;\n abortSignal?: AbortSignal;\n onEvent?: (event: EmbeddedStreamEvent) => void;\n getConfig?: () => Config | undefined;\n beforeTurn?: () => void | Promise<void>;\n afterTurn?: (userPlain: string) => void | Promise<void>;\n startupAction?: 'new' | 'reset';\n forceStartupContext?: boolean;\n applyStartupContext?: boolean;\n /** When true, dispose session MCP runtime after this turn completes. */\n cleanupBundleMcpOnRunEnd?: boolean;\n};\n\nexport async function runEmbeddedTurnForSession(\n params: RunEmbeddedForSessionParams,\n): Promise<RunXopcEmbeddedTurnResult> {\n const { sessionKey, agentManager, modelManager, sessionStore, userMessage } = params;\n const runId = params.runId ?? crypto.randomUUID();\n\n await params.beforeTurn?.();\n\n const agent = (agentManager as any).getOrCreateAgent(sessionKey) as {\n state: {\n tools: RunXopcEmbeddedTurnParams['tools'];\n systemPrompt?: string;\n thinkingLevel?: ThinkingLevel;\n };\n };\n const mm = modelManager as any;\n await mm.applyModelForSession(agent, sessionKey);\n\n const modelRef = String(mm.getModelForSession(sessionKey));\n agentManager.setModelForSession(sessionKey, modelRef);\n const model = mm.getResolvedModelForSession(sessionKey) as RunXopcEmbeddedTurnParams['model'];\n const tools = agent.state.tools;\n const systemPrompt = agent.state.systemPrompt ?? '';\n const thinkingLevel = (params.thinkingOverride as ThinkingLevel | undefined) ?? agent.state.thinkingLevel;\n const workspaceDir = agentManager.getResolvedWorkspaceForSession(sessionKey);\n const config = params.getConfig?.();\n\n // --- Pre-turn automatic compaction ---\n await maybeAutoCompactBeforeTurn({\n sessionKey,\n sessionStore,\n agentManager,\n model,\n config,\n onEvent: params.onEvent,\n });\n\n let userMessageForTurn = userMessage;\n if (params.applyStartupContext !== false) {\n userMessageForTurn = await applyStartupContextToUserMessage({\n userMessage,\n sessionKey,\n workspaceDir,\n cfg: config,\n sessionStore,\n startupAction: params.startupAction,\n force: params.forceStartupContext,\n });\n }\n\n const result = await (async () => {\n const mcpResolved = await resolveEmbeddedMcpToolsForTurn({\n sessionKey,\n workspaceDir,\n cfg: config,\n baseTools: tools,\n cleanupOnTurnEnd: params.cleanupBundleMcpOnRunEnd === true,\n });\n const turnTools = mergeTurnTools(tools, mcpResolved.tools);\n try {\n return await runXopcEmbeddedTurn({\n sessionKey,\n runId,\n userMessage: userMessageForTurn,\n images: params.llmImages,\n model,\n modelRef,\n tools: turnTools,\n systemPrompt,\n thinkingLevel,\n workspaceDir,\n sessionStore,\n timeoutMs: resolveAgentTurnTimeoutMs(config),\n abortSignal: params.abortSignal,\n onEvent: params.onEvent,\n });\n } finally {\n await mcpResolved.dispose().catch(() => {});\n }\n })();\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Pre-turn automatic compaction\n// ---------------------------------------------------------------------------\n\nasync function maybeAutoCompactBeforeTurn(opts: {\n sessionKey: string;\n sessionStore: SessionStore;\n agentManager: AgentInstanceGateway;\n model: RunXopcEmbeddedTurnParams['model'];\n config: Config | undefined;\n onEvent?: (event: EmbeddedStreamEvent) => void;\n}): Promise<void> {\n const { sessionKey, sessionStore, agentManager, model, config, onEvent } = opts;\n if (config) {\n try {\n const manifest = resolveEffectiveAgentManifestForSession(config, sessionKey);\n if (manifest.memory.retention?.compaction === false) {\n return;\n }\n } catch {\n // Keep compaction enabled when manifest resolution is unavailable.\n }\n }\n\n const contextWindow = (model as { contextWindow?: number }).contextWindow ?? 128_000;\n const messages = await sessionStore.load(sessionKey);\n const prep = sessionStore.prepareCompaction(sessionKey, messages, contextWindow);\n\n if (!prep.needsCompaction) {\n return;\n }\n\n log.info(\n { sessionKey, reason: prep.stats?.reason, usagePercent: prep.stats?.usagePercent, contextWindow },\n 'Pre-turn auto-compaction triggered',\n );\n\n onEvent?.({\n type: 'compaction',\n status: 'started',\n tokensBefore: prep.stats?.usagePercent != null\n ? Math.round((prep.stats.usagePercent as number) * contextWindow)\n : undefined,\n });\n\n try {\n const result = await sessionStore.compact(sessionKey, messages, contextWindow);\n\n if (result.compacted) {\n // Evict the cached agent so the next turn reloads from the compacted transcript\n agentManager.removeAgent(sessionKey);\n\n log.info(\n { sessionKey, tokensBefore: result.tokensBefore, tokensAfter: result.tokensAfter },\n 'Pre-turn auto-compaction completed',\n );\n\n onEvent?.({\n type: 'compaction',\n status: 'completed',\n tokensBefore: result.tokensBefore,\n tokensAfter: result.tokensAfter,\n summary: result.summary.length > 200 ? `${result.summary.slice(0, 200)}…` : result.summary,\n });\n } else {\n onEvent?.({ type: 'compaction', status: 'skipped' });\n }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.warn({ err, sessionKey }, `Pre-turn auto-compaction failed: ${errorMessage}`);\n // Non-fatal: let the turn proceed even if compaction fails\n onEvent?.({ type: 'compaction', status: 'skipped' });\n }\n}\n"],"mappings":"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"}
@@ -3,16 +3,15 @@ import { normalizeProviderId, parseModelRef as parseModelRef$1 } from "../models
3
3
  //#region src/agent/fallback/candidates.ts
4
4
  init_providers();
5
5
  function getDefaultModelParts(config) {
6
- const parts = getDefaultModelSync(config).split("/");
6
+ const defaultModel = getDefaultModelSync(config);
7
+ if (!defaultModel) return null;
8
+ const parts = defaultModel.split("/");
9
+ if (!parts[0] || !parts[1]) return null;
7
10
  return {
8
- provider: parts[0] || "anthropic",
9
- model: parts[1] || "claude-sonnet-4-5"
11
+ provider: parts[0],
12
+ model: parts[1]
10
13
  };
11
14
  }
12
- function getConfiguredFallbackRefs(config) {
13
- const legacyModel = (config?.agents)?.defaults?.model;
14
- if (legacyModel && typeof legacyModel === "object" && Array.isArray(legacyModel.fallbacks)) return legacyModel.fallbacks.filter((value) => typeof value === "string");
15
- }
16
15
  /**
17
16
  * Parse model reference string into provider/model parts.
18
17
  * Uses the unified implementation from selection.ts.
@@ -28,11 +27,11 @@ function parseModelRef(raw, defaultProvider) {
28
27
  function resolveFallbackCandidates(params) {
29
28
  const { cfg, provider: inputProvider, model: inputModel, fallbacksOverride } = params;
30
29
  const primaryRef = getDefaultModelSync(cfg);
31
- const fallbacks = fallbacksOverride ?? getConfiguredFallbackRefs(cfg);
30
+ const fallbacks = fallbacksOverride;
32
31
  const defaultParts = getDefaultModelParts(cfg);
33
32
  const primaryResolved = parseModelRef(primaryRef || getDefaultModelSync(cfg));
34
- const provider = inputProvider.trim() || primaryResolved?.provider || defaultParts.provider;
35
- const model = inputModel.trim() || primaryResolved?.model || defaultParts.model;
33
+ const provider = inputProvider.trim() || primaryResolved?.provider || defaultParts?.provider || "";
34
+ const model = inputModel.trim() || primaryResolved?.model || defaultParts?.model || "";
36
35
  const candidates = [];
37
36
  const seen = /* @__PURE__ */ new Set();
38
37
  const addCandidate = (c) => {
@@ -42,7 +41,7 @@ function resolveFallbackCandidates(params) {
42
41
  seen.add(key);
43
42
  candidates.push(c);
44
43
  };
45
- addCandidate({
44
+ if (provider && model) addCandidate({
46
45
  provider,
47
46
  model
48
47
  });
@@ -1 +1 @@
1
- {"version":3,"file":"candidates.js","names":["parseModelRefUtil"],"sources":["../../../../src/agent/fallback/candidates.ts"],"sourcesContent":["import type { Config } from '../../config/schema.js';\nimport { isProviderConfiguredSync } from '../../providers/index.js';\nimport { getDefaultModelSync } from '../../providers/index.js';\nimport { parseModelRef as parseModelRefUtil, normalizeProviderId } from '../models/selection.js';\n\nexport interface ModelCandidate {\n provider: string;\n model: string;\n}\n\nexport interface FallbackAttempt {\n provider: string;\n model: string;\n error: string;\n reason?: string;\n status?: number;\n code?: string;\n}\n\n// Get default model dynamically\nfunction getDefaultModelParts(config?: Config): { provider: string; model: string } {\n const defaultModel = getDefaultModelSync(config);\n const parts = defaultModel.split('/');\n return {\n provider: parts[0] || 'anthropic',\n model: parts[1] || 'claude-sonnet-4-5',\n };\n}\n\nfunction getConfiguredFallbackRefs(config?: Config): string[] | undefined {\n const legacyModel = (config?.agents as unknown as {\n defaults?: { model?: { fallbacks?: unknown } };\n } | undefined)?.defaults?.model;\n if (\n legacyModel &&\n typeof legacyModel === 'object' &&\n Array.isArray(legacyModel.fallbacks)\n ) {\n return legacyModel.fallbacks.filter((value): value is string => typeof value === 'string');\n }\n return undefined;\n}\n\n/**\n * Parse model reference string into provider/model parts.\n * Uses the unified implementation from selection.ts.\n */\nfunction parseModelRef(raw: string, defaultProvider?: string): ModelCandidate | null {\n const result = parseModelRefUtil(raw, defaultProvider);\n if (!result) return null;\n return {\n provider: normalizeProviderId(result.provider),\n model: result.model,\n };\n}\n\nexport function resolveFallbackCandidates(params: {\n cfg: Config | undefined;\n provider: string;\n model: string;\n fallbacksOverride?: string[];\n}): ModelCandidate[] {\n const { cfg, provider: inputProvider, model: inputModel, fallbacksOverride } = params;\n\n const primaryRef = getDefaultModelSync(cfg);\n const fallbacks = fallbacksOverride ?? getConfiguredFallbackRefs(cfg);\n\n const defaultParts = getDefaultModelParts(cfg);\n const primaryResolved = parseModelRef(primaryRef || getDefaultModelSync(cfg));\n const provider = inputProvider.trim() || primaryResolved?.provider || defaultParts.provider;\n const model = inputModel.trim() || primaryResolved?.model || defaultParts.model;\n\n const candidates: ModelCandidate[] = [];\n const seen = new Set<string>();\n\n const addCandidate = (c: ModelCandidate) => {\n const key = `${c.provider}/${c.model}`.toLowerCase();\n if (seen.has(key)) return;\n // Skip providers that are not configured (no API key)\n if (!isProviderConfiguredSync(c.provider)) return;\n seen.add(key);\n candidates.push(c);\n };\n\n addCandidate({ provider, model });\n\n if (fallbacks) {\n for (const fb of fallbacks) {\n const resolved = parseModelRef(fb, provider);\n if (resolved) addCandidate(resolved);\n }\n }\n\n if (!fallbacksOverride && primaryResolved) {\n addCandidate(primaryResolved);\n }\n\n return candidates;\n}\n"],"mappings":";;;gBACoE;AAmBpE,SAAS,qBAAqB,QAAsD;CAElF,MAAM,QADe,oBAAoB,OACf,CAAC,MAAM,IAAI;AACrC,QAAO;EACL,UAAU,MAAM,MAAM;EACtB,OAAO,MAAM,MAAM;EACpB;;AAGH,SAAS,0BAA0B,QAAuC;CACxE,MAAM,eAAe,QAAQ,SAEb,UAAU;AAC1B,KACE,eACA,OAAO,gBAAgB,YACvB,MAAM,QAAQ,YAAY,UAAU,CAEpC,QAAO,YAAY,UAAU,QAAQ,UAA2B,OAAO,UAAU,SAAS;;;;;;AAS9F,SAAS,cAAc,KAAa,iBAAiD;CACnF,MAAM,SAASA,gBAAkB,KAAK,gBAAgB;AACtD,KAAI,CAAC,OAAQ,QAAO;AACpB,QAAO;EACL,UAAU,oBAAoB,OAAO,SAAS;EAC9C,OAAO,OAAO;EACf;;AAGH,SAAgB,0BAA0B,QAKrB;CACnB,MAAM,EAAE,KAAK,UAAU,eAAe,OAAO,YAAY,sBAAsB;CAE/E,MAAM,aAAa,oBAAoB,IAAI;CAC3C,MAAM,YAAY,qBAAqB,0BAA0B,IAAI;CAErE,MAAM,eAAe,qBAAqB,IAAI;CAC9C,MAAM,kBAAkB,cAAc,cAAc,oBAAoB,IAAI,CAAC;CAC7E,MAAM,WAAW,cAAc,MAAM,IAAI,iBAAiB,YAAY,aAAa;CACnF,MAAM,QAAQ,WAAW,MAAM,IAAI,iBAAiB,SAAS,aAAa;CAE1E,MAAM,aAA+B,EAAE;CACvC,MAAM,uBAAO,IAAI,KAAa;CAE9B,MAAM,gBAAgB,MAAsB;EAC1C,MAAM,MAAM,GAAG,EAAE,SAAS,GAAG,EAAE,QAAQ,aAAa;AACpD,MAAI,KAAK,IAAI,IAAI,CAAE;AAEnB,MAAI,CAAC,yBAAyB,EAAE,SAAS,CAAE;AAC3C,OAAK,IAAI,IAAI;AACb,aAAW,KAAK,EAAE;;AAGpB,cAAa;EAAE;EAAU;EAAO,CAAC;AAEjC,KAAI,UACF,MAAK,MAAM,MAAM,WAAW;EAC1B,MAAM,WAAW,cAAc,IAAI,SAAS;AAC5C,MAAI,SAAU,cAAa,SAAS;;AAIxC,KAAI,CAAC,qBAAqB,gBACxB,cAAa,gBAAgB;AAG/B,QAAO"}
1
+ {"version":3,"file":"candidates.js","names":["parseModelRefUtil"],"sources":["../../../../src/agent/fallback/candidates.ts"],"sourcesContent":["import type { Config } from '../../config/schema.js';\nimport { isProviderConfiguredSync } from '../../providers/index.js';\nimport { getDefaultModelSync } from '../../providers/index.js';\nimport { parseModelRef as parseModelRefUtil, normalizeProviderId } from '../models/selection.js';\n\nexport interface ModelCandidate {\n provider: string;\n model: string;\n}\n\nexport interface FallbackAttempt {\n provider: string;\n model: string;\n error: string;\n reason?: string;\n status?: number;\n code?: string;\n}\n\nfunction getDefaultModelParts(config?: Config): ModelCandidate | null {\n const defaultModel = getDefaultModelSync(config);\n if (!defaultModel) return null;\n const parts = defaultModel.split('/');\n if (!parts[0] || !parts[1]) return null;\n return { provider: parts[0], model: parts[1] };\n}\n\n/**\n * Parse model reference string into provider/model parts.\n * Uses the unified implementation from selection.ts.\n */\nfunction parseModelRef(raw: string, defaultProvider?: string): ModelCandidate | null {\n const result = parseModelRefUtil(raw, defaultProvider);\n if (!result) return null;\n return {\n provider: normalizeProviderId(result.provider),\n model: result.model,\n };\n}\n\nexport function resolveFallbackCandidates(params: {\n cfg: Config | undefined;\n provider: string;\n model: string;\n fallbacksOverride?: string[];\n}): ModelCandidate[] {\n const { cfg, provider: inputProvider, model: inputModel, fallbacksOverride } = params;\n\n const primaryRef = getDefaultModelSync(cfg);\n const fallbacks = fallbacksOverride;\n\n const defaultParts = getDefaultModelParts(cfg);\n const primaryResolved = parseModelRef(primaryRef || getDefaultModelSync(cfg));\n const provider = inputProvider.trim() || primaryResolved?.provider || defaultParts?.provider || '';\n const model = inputModel.trim() || primaryResolved?.model || defaultParts?.model || '';\n\n const candidates: ModelCandidate[] = [];\n const seen = new Set<string>();\n\n const addCandidate = (c: ModelCandidate) => {\n const key = `${c.provider}/${c.model}`.toLowerCase();\n if (seen.has(key)) return;\n // Skip providers that are not configured (no API key)\n if (!isProviderConfiguredSync(c.provider)) return;\n seen.add(key);\n candidates.push(c);\n };\n\n if (provider && model) {\n addCandidate({ provider, model });\n }\n\n if (fallbacks) {\n for (const fb of fallbacks) {\n const resolved = parseModelRef(fb, provider);\n if (resolved) addCandidate(resolved);\n }\n }\n\n if (!fallbacksOverride && primaryResolved) {\n addCandidate(primaryResolved);\n }\n\n return candidates;\n}\n"],"mappings":";;;gBACoE;AAkBpE,SAAS,qBAAqB,QAAwC;CACpE,MAAM,eAAe,oBAAoB,OAAO;AAChD,KAAI,CAAC,aAAc,QAAO;CAC1B,MAAM,QAAQ,aAAa,MAAM,IAAI;AACrC,KAAI,CAAC,MAAM,MAAM,CAAC,MAAM,GAAI,QAAO;AACnC,QAAO;EAAE,UAAU,MAAM;EAAI,OAAO,MAAM;EAAI;;;;;;AAOhD,SAAS,cAAc,KAAa,iBAAiD;CACnF,MAAM,SAASA,gBAAkB,KAAK,gBAAgB;AACtD,KAAI,CAAC,OAAQ,QAAO;AACpB,QAAO;EACL,UAAU,oBAAoB,OAAO,SAAS;EAC9C,OAAO,OAAO;EACf;;AAGH,SAAgB,0BAA0B,QAKrB;CACnB,MAAM,EAAE,KAAK,UAAU,eAAe,OAAO,YAAY,sBAAsB;CAE/E,MAAM,aAAa,oBAAoB,IAAI;CAC3C,MAAM,YAAY;CAElB,MAAM,eAAe,qBAAqB,IAAI;CAC9C,MAAM,kBAAkB,cAAc,cAAc,oBAAoB,IAAI,CAAC;CAC7E,MAAM,WAAW,cAAc,MAAM,IAAI,iBAAiB,YAAY,cAAc,YAAY;CAChG,MAAM,QAAQ,WAAW,MAAM,IAAI,iBAAiB,SAAS,cAAc,SAAS;CAEpF,MAAM,aAA+B,EAAE;CACvC,MAAM,uBAAO,IAAI,KAAa;CAE9B,MAAM,gBAAgB,MAAsB;EAC1C,MAAM,MAAM,GAAG,EAAE,SAAS,GAAG,EAAE,QAAQ,aAAa;AACpD,MAAI,KAAK,IAAI,IAAI,CAAE;AAEnB,MAAI,CAAC,yBAAyB,EAAE,SAAS,CAAE;AAC3C,OAAK,IAAI,IAAI;AACb,aAAW,KAAK,EAAE;;AAGpB,KAAI,YAAY,MACd,cAAa;EAAE;EAAU;EAAO,CAAC;AAGnC,KAAI,UACF,MAAK,MAAM,MAAM,WAAW;EAC1B,MAAM,WAAW,cAAc,IAAI,SAAS;AAC5C,MAAI,SAAU,cAAa,SAAS;;AAIxC,KAAI,CAAC,qBAAqB,gBACxB,cAAa,gBAAgB;AAG/B,QAAO"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Bounded curated memory: agent home `memories/MEMORY.md` + `USER.md`, §-delimited entries.
2
+ * Bounded curated memory: agent home `memories/MEMORY.md` + global `user/MEMORY.md`, §-delimited entries.
3
3
  * Snapshot for system prompt is captured at load time and not mutated until next load.
4
4
  */
5
5
  import type { MemorySnapshot, MemoryStoreConfig } from './types.js';
@@ -10,10 +10,12 @@ export declare class BuiltinMemoryStore {
10
10
  private userEntries;
11
11
  private snapshot;
12
12
  constructor(config: MemoryStoreConfig);
13
- /** When false, USER.md is omitted from the snapshot and should not be edited via `curated_memory`. */
13
+ /** When false, global user memory is omitted from the snapshot and should not be edited via `curated_memory`. */
14
14
  isUserProfileEnabled(): boolean;
15
15
  /** Agent-scoped curated memory directory (`~/.xopc/agents/<id>/memories/`). */
16
16
  get memoriesDir(): string;
17
+ /** Global user memory file (`~/.xopc/user/MEMORY.md`). */
18
+ get userMemoryPath(): string;
17
19
  /** Workspace whose MEMORY.md and daily notes participate in local memory search. */
18
20
  get workspaceDir(): string;
19
21
  pathFor(target: 'memory' | 'user'): string;
@@ -1,10 +1,10 @@
1
- import { join } from "node:path";
1
+ import { dirname, join } from "node:path";
2
2
  import { existsSync, mkdirSync, readFileSync } from "node:fs";
3
3
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
4
4
  import lockfile from "proper-lockfile";
5
5
  //#region src/agent/memory/builtin-memory-store.ts
6
6
  /**
7
- * Bounded curated memory: agent home `memories/MEMORY.md` + `USER.md`, §-delimited entries.
7
+ * Bounded curated memory: agent home `memories/MEMORY.md` + global `user/MEMORY.md`, §-delimited entries.
8
8
  * Snapshot for system prompt is captured at load time and not mutated until next load.
9
9
  */
10
10
  const MEMORY_ENTRY_DELIMITER = "\n§\n";
@@ -20,7 +20,7 @@ var BuiltinMemoryStore = class {
20
20
  constructor(config) {
21
21
  this.config = config;
22
22
  }
23
- /** When false, USER.md is omitted from the snapshot and should not be edited via `curated_memory`. */
23
+ /** When false, global user memory is omitted from the snapshot and should not be edited via `curated_memory`. */
24
24
  isUserProfileEnabled() {
25
25
  return this.config.userProfileEnabled !== false;
26
26
  }
@@ -28,12 +28,16 @@ var BuiltinMemoryStore = class {
28
28
  get memoriesDir() {
29
29
  return this.config.memoriesDir;
30
30
  }
31
+ /** Global user memory file (`~/.xopc/user/MEMORY.md`). */
32
+ get userMemoryPath() {
33
+ return this.config.userMemoryPath;
34
+ }
31
35
  /** Workspace whose MEMORY.md and daily notes participate in local memory search. */
32
36
  get workspaceDir() {
33
37
  return this.config.workspaceDir;
34
38
  }
35
39
  pathFor(target) {
36
- return join(this.memoriesDir, target === "memory" ? "MEMORY.md" : "USER.md");
40
+ return target === "memory" ? join(this.memoriesDir, "MEMORY.md") : this.userMemoryPath;
37
41
  }
38
42
  /**
39
43
  * Load from disk and freeze {@link getSnapshot}. Sync for agent creation (prefix cache stability).
@@ -41,7 +45,7 @@ var BuiltinMemoryStore = class {
41
45
  loadFromDiskSync() {
42
46
  mkdirSync(this.memoriesDir, { recursive: true });
43
47
  this.memoryEntries = this.parseFileContent(this.readPathSync(join(this.memoriesDir, "MEMORY.md")));
44
- this.userEntries = this.config.userProfileEnabled === false ? [] : this.parseFileContent(this.readPathSync(join(this.memoriesDir, "USER.md")));
48
+ this.userEntries = this.config.userProfileEnabled === false ? [] : this.parseFileContent(this.readPathSync(this.userMemoryPath));
45
49
  this.memoryEntries = dedupePreserveOrder(this.memoryEntries);
46
50
  this.userEntries = dedupePreserveOrder(this.userEntries);
47
51
  this.snapshot = {
@@ -185,7 +189,7 @@ var BuiltinMemoryStore = class {
185
189
  }
186
190
  async withFileLock(target, fn) {
187
191
  const filePath = this.pathFor(target);
188
- await mkdir(this.memoriesDir, { recursive: true });
192
+ await mkdir(dirname(filePath), { recursive: true });
189
193
  if (!existsSync(filePath)) await writeFile(filePath, "", "utf-8");
190
194
  await lockfile.lock(filePath, {
191
195
  retries: 3,
@@ -214,6 +218,7 @@ var BuiltinMemoryStore = class {
214
218
  const filePath = this.pathFor(target);
215
219
  const content = entries.join(MEMORY_ENTRY_DELIMITER);
216
220
  const tmpPath = `${filePath}.tmp.${Date.now()}`;
221
+ await mkdir(dirname(filePath), { recursive: true });
217
222
  await writeFile(tmpPath, content, "utf-8");
218
223
  await rename(tmpPath, filePath);
219
224
  }