@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":"service.js","names":[],"sources":["../../../src/agent/service.ts"],"sourcesContent":["import type { AgentEvent, AgentMessage, ThinkingLevel } from '@earendil-works/pi-agent-core';\nimport type { MessageBus } from '../infra/bus/index.js';\nimport { type Config, getAgentDefaultModelRef } from '../config/schema.js';\nimport {\n maybeRefineSessionTitleWithLlm,\n maybeSetProvisionalSessionTitle,\n} from '../session/session-title.js';\nimport type { ChannelManager } from '../channels/manager.js';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport {\n SessionStore,\n SessionConfigStore,\n onSessionTranscriptUpdate,\n effectiveWorkspacePathForSession,\n type CompactionConfig,\n type WindowConfig,\n} from '../session/index.js';\nimport { type ThinkLevel } from './transcript/thinking-types.js';\nimport { createLogger } from '../utils/logger.js';\nimport { ExtensionHookRunner } from '../extensions/index.js';\nimport { extractTextContent } from './context/workspace.js';\nimport { SessionTracker } from './session/tracker.js';\nimport { ModelManager } from './models/index.js';\nimport { initializeCommands } from '../chat-commands/index.js';\nimport { ProgressFeedbackManager } from './lifecycle/progress.js';\nimport { HookHandler } from './lifecycle/hook-handler.js';\nimport { ToolErrorTracker } from './tools/error-tracker.js';\nimport { RequestLimiter } from './models/request-limiter.js';\nimport { SystemReminder } from './prompt/system-reminder.js';\nimport { ToolUsageAnalyzer } from './tools/usage-analyzer.js';\nimport { ToolChainTracker } from './tools/chain-tracker.js';\nimport { ErrorPatternMatcher } from './tools/error-pattern-matcher.js';\nimport { ContextMiddleware, SelfVerifyMiddleware } from './middleware/index.js';\nimport { LifecycleManager } from './lifecycle/index.js';\nimport { CompactionLifecycleHandler } from './lifecycle/handlers/compaction.js';\n\nimport {\n MessageRouter,\n CommandHandler,\n StreamManager,\n OutboundCoordinator,\n} from './messaging/index.js';\nimport { InboundLoop } from './inbound/inbound-loop.js';\nimport { TurnDispatcher } from './inbound/turn-dispatcher.js';\nimport {\n SessionContextManager,\n SessionLifecycleManager,\n SessionStateBag,\n SessionConfigService,\n SessionHydrator,\n SessionInspector,\n type SessionContext,\n} from './session/index.js';\nimport { AgentOrchestrator, AgentEventHandler } from './orchestration/index.js';\nimport {\n getWorkflowProgressBroker,\n type BrokerListenerHandle,\n} from './workflow/index.js';\nimport { FeedbackCoordinator } from './feedback/index.js';\nimport {\n AgentManager,\n type AgentSkillAvailabilityPayload,\n type SkillCatalogEntry,\n type SkillCatalogSnapshot,\n} from './agent-manager.js';\nimport type { SkillMarkdownPreviewPayload } from './skills/types.js';\nimport type { AgentServiceConfig, StreamHandle } from './service.types.js';\nimport { PersistentGoalService } from './goals/persistent-goal-service.js';\nimport { reconcileManagedDreamingCronJobs } from './service/reconcile-dreaming-cron.js';\nimport { parseNoteAttachmentTarget } from '../notes/attachment-ref.js';\n\nimport {\n resolveAgentHomeDir,\n resolveAgentProfileDir,\n resolveDefaultAgentId,\n} from './agent-scope.js';\nimport {\n extractProfileAgentId,\n resolveEffectiveAgentProfileForSession,\n} from '../config/agent-profile.js';\nimport { cleanTrailingErrors } from './memory/message-sanitizer.js';\nimport { MemoryFlushService } from './memory/memory-flush.js';\nimport type { MemoryFlushConfig } from './memory/memory-flush.js';\nimport { tryApplySessionTranscriptHygiene } from './transcript/transcript-hygiene.js';\nimport {\n persistInboundAttachments,\n type InboundAttachmentInput,\n type MediaRef,\n} from '../channels/attachments/inbound-persist.js';\nimport { applyConfigOverrides } from '../config/runtime-overrides.js';\n\nexport type { AgentServiceConfig, AgentContext, StreamHandle } from './service.types.js';\n\nconst log = createLogger('AgentService');\n\nfunction assistantMessageText(message: AgentMessage): string {\n const content = (message as { content?: unknown }).content;\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n return content\n .map((block) => {\n if (!block || typeof block !== 'object') return '';\n const rec = block as { type?: unknown; text?: unknown };\n return rec.type === 'text' && typeof rec.text === 'string' ? rec.text : '';\n })\n .join('');\n}\n\nexport class AgentService {\n /**\n * Persistent transcript + session-metadata store. Public so the gateway/TUI\n * can read sessions, delete them, etc. without forcing every CRUD-style\n * operation through a delegation method on `AgentService`.\n */\n readonly sessionStore: SessionStore;\n private sessionConfigStore: SessionConfigStore;\n private hookRunner?: ExtensionHookRunner;\n private agentId: string;\n private workspaceDir: string;\n private channelManagerRef: ChannelManager | null = null;\n private bus: MessageBus;\n private config: AgentServiceConfig;\n private sessionTracker: SessionTracker;\n private modelManager: ModelManager;\n private progressManager: ProgressFeedbackManager;\n private hookHandler: HookHandler;\n private lifecycleManager: LifecycleManager;\n private errorTracker: ToolErrorTracker;\n private requestLimiter: RequestLimiter;\n private systemReminder: SystemReminder;\n private toolUsageAnalyzer: ToolUsageAnalyzer;\n private toolChainTracker: ToolChainTracker;\n private errorPatternMatcher: ErrorPatternMatcher;\n private selfVerifyMiddleware: SelfVerifyMiddleware;\n private contextMiddleware: ContextMiddleware;\n\n private messageRouter: MessageRouter;\n private commandHandler: CommandHandler;\n private streamManager: StreamManager;\n /**\n * Outbound pipeline: typing controller, silence guard, final response publish,\n * extension `message_sending`/`message_sent` hooks, post-turn `webchat_turn_complete`\n * event. Public so the gateway / channels can drive it directly.\n */\n readonly outboundCoordinator: OutboundCoordinator;\n private inboundLoop: InboundLoop;\n /**\n * Direct-turn entry points: `processDirect` (one-shot), `processDirectStreaming`\n * (SSE generator), webchat steering and SSE injection. Public so the gateway,\n * TUI, CLI, and cron jobs do not need to thread every call through `AgentService`.\n */\n readonly turnDispatcher: TurnDispatcher;\n /**\n * `/goal` runtime: continuation scheduling, persistent-goal API factory,\n * stream-outcome state, post-turn verdict. Public so the gateway can wire\n * the webchat continuation scheduler and read stream outcomes directly.\n */\n readonly persistentGoals: PersistentGoalService;\n /**\n * Per-session config writes (model / thinking / reasoning / working directory).\n * Public so REST endpoints and CLI flows can hit it without going through a\n * monolithic patch entrypoint on `AgentService`.\n */\n readonly sessionConfig: SessionConfigService;\n /**\n * Hydration — read persisted per-session config and apply it to the runtime\n * (AgentManager / ModelManager). The mirror image of `sessionConfig`: writes\n * go through `sessionConfig`, reads-into-runtime go through `sessionHydrator`.\n */\n readonly sessionHydrator: SessionHydrator;\n /**\n * Read-only introspection (compaction, /context report, /btw, contextUsage,\n * agentConfig view). Public so REST endpoints and CLI flows can query a\n * session's view without going through delegating methods on `AgentService`.\n */\n readonly sessionInspector: SessionInspector;\n private sessionContextManager: SessionContextManager;\n private sessionLifecycleManager: SessionLifecycleManager;\n private agentOrchestrator: AgentOrchestrator;\n private agentEventHandler: AgentEventHandler;\n private workflowProgressBrokerHandle: BrokerListenerHandle | null = null;\n private feedbackCoordinator: FeedbackCoordinator;\n private agentManager: AgentManager;\n private memoryFlushService: MemoryFlushService;\n\n /**\n * Unified per-session state container (replaces six ad-hoc Maps). Owns webchat\n * publishers, last assistant text, embedded stream buffer, persistent-goal stream\n * outcomes, concurrent-turn depth, and event-listener unsubscribers; runs a TTL\n * sweep for slots that have no explicit owner.\n */\n private sessionState = new SessionStateBag();\n\n /** Gateway: notify UI after direct `SessionStore.updateMetadata` (no SessionManager emit). */\n private onSessionMetadataUpdated?: (sessionKey: string, patch?: { name?: string }) => void;\n private onSessionTranscriptUpdated?: (sessionKey: string) => void;\n\n private effectiveAppConfig(): Config | undefined {\n const base = this.config.config;\n return base ? applyConfigOverrides(base) : undefined;\n }\n\n constructor(bus: MessageBus, config: AgentServiceConfig) {\n this.bus = bus;\n this.config = config;\n this.onSessionMetadataUpdated = config.onSessionMetadataUpdated;\n this.onSessionTranscriptUpdated = config.onSessionTranscriptUpdated;\n this.agentId = `agent-${Date.now()}`;\n this.workspaceDir = config.workspace;\n\n this.sessionTracker = new SessionTracker();\n this.modelManager = new ModelManager({\n defaultModel: config.model,\n config: config.config,\n });\n\n initializeCommands();\n log.debug('Command system initialized');\n\n this.sessionStore = config.sessionStore ?? this.createSessionStore();\n onSessionTranscriptUpdate((update) => {\n void this.sessionStore.syncEmbeddedTranscriptUpdate(update).catch((err) => {\n log.warn(\n { err, sessionKey: update.sessionKey },\n 'Transcript index sync failed',\n );\n });\n const sk = update.sessionKey?.trim();\n if (sk) {\n this.onSessionTranscriptUpdated?.(sk);\n }\n });\n const appCfgForPaths = this.config.config;\n if (!appCfgForPaths) {\n throw new Error('AgentService requires config.config for session paths');\n }\n const defaultAid = resolveDefaultAgentId(appCfgForPaths);\n const defaultAgentHome = resolveAgentHomeDir(appCfgForPaths, defaultAid);\n this.sessionConfigStore = new SessionConfigStore(defaultAgentHome);\n\n this.hookRunner = this.createHookRunner();\n this.hookHandler = new HookHandler({\n hookRunner: this.hookRunner,\n agentId: this.agentId,\n get sessionKey() { return this.currentContext?.sessionKey; },\n });\n\n this.progressManager = this.createProgressManager();\n this.initializeReliabilityModules();\n\n this.lifecycleManager = new LifecycleManager();\n this.initializeLifecycleHandlers();\n\n this.streamManager = new StreamManager();\n this.sessionContextManager = new SessionContextManager();\n this.feedbackCoordinator = new FeedbackCoordinator({\n progressManager: this.progressManager,\n bus,\n });\n\n // Initialize AgentManager\n this.memoryFlushService = new MemoryFlushService();\n\n this.agentManager = new AgentManager({\n workspace: config.workspace,\n model: config.model,\n config: config.config,\n extensionRegistry: config.extensionRegistry,\n hookRunner: this.hookRunner,\n bus,\n getCurrentContext: () => this.sessionContextManager.getContext(),\n getSessionStore: () => this.sessionStore,\n getModelManager: () => this.modelManager,\n thinkingLevel: config.thinkingLevel,\n reasoningLevel: config.reasoningLevel,\n verboseLevel: config.verboseLevel,\n gatewayClarify: config.gatewayClarify,\n getCronService: config.getCronService,\n getWorkflowRunService: config.getWorkflowRunService,\n onSkillsUpdated: config.onSkillsUpdated,\n isWorkspaceTrusted: config.isWorkspaceTrusted,\n });\n\n this.agentEventHandler = new AgentEventHandler({\n progressManager: this.progressManager,\n errorTracker: this.errorTracker,\n requestLimiter: this.requestLimiter,\n lifecycleManager: this.lifecycleManager,\n toolChainTracker: this.toolChainTracker,\n selfVerifyMiddleware: this.selfVerifyMiddleware,\n systemReminder: this.systemReminder,\n toolUsageAnalyzer: this.toolUsageAnalyzer,\n errorPatternMatcher: this.errorPatternMatcher,\n });\n\n // Wire the workflow progress broker into the session event bus. Channel\n // extensions self-register their capability against the singleton at\n // boot; the broker only forwards updates to whatever has registered, so\n // this is safe even when no channel is configured.\n this.workflowProgressBrokerHandle = getWorkflowProgressBroker().attachTo(\n this.agentEventHandler,\n );\n\n // sessionHydrator is constructed early because AgentOrchestrator + InboundLoop +\n // TurnDispatcher all need it; the SessionConfigService instance below also\n // shares the same constructor parameters.\n this.sessionHydrator = new SessionHydrator({\n sessionConfigStore: this.sessionConfigStore,\n agentManager: this.agentManager,\n modelManager: this.modelManager,\n getConfig: () => this.effectiveAppConfig(),\n });\n\n this.agentOrchestrator = new AgentOrchestrator({\n agentManager: this.agentManager,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n eventHandler: this.agentEventHandler,\n feedbackCoordinator: this.feedbackCoordinator,\n sessionConfigStore: this.sessionConfigStore,\n sessionHydrator: this.sessionHydrator,\n getConfig: () => this.effectiveAppConfig(),\n getThinkingDefault: () => undefined,\n getThinkingDefaultForSession: (sessionKey: string) =>\n this.agentManager.getThinkingDefaultForSession(sessionKey),\n workspaceRoot: this.workspaceDir,\n getWorkspaceRootForSession: (sessionKey: string) =>\n this.agentManager.getResolvedWorkspaceForSession(sessionKey),\n getAgentInternalStorageRootForSession: (sessionKey: string) =>\n resolveAgentHomeDir(this.config.config!, extractProfileAgentId(sessionKey, this.config.config!)),\n enqueueAutoTitle: (sessionKey: string) => this.enqueueMaybeAutoTitleAfterPersist(sessionKey),\n onEmbeddedStreamEvent: (sessionKey, event) => {\n const ctx = this.sessionContextManager.getContext();\n if (!ctx || ctx.sessionKey !== sessionKey) {\n return;\n }\n if (event.type === 'message_update' && event.message.role === 'assistant') {\n const next = assistantMessageText(event.message);\n this.streamManager.update(next);\n }\n },\n onEmbeddedTurnComplete: (sessionKey, text) => {\n if (text) {\n this.sessionState.setLastAssistantText(sessionKey, text);\n }\n this.sessionState.clearEmbeddedStreamText(sessionKey);\n },\n });\n\n this.messageRouter = new MessageRouter();\n this.commandHandler = new CommandHandler({\n config: config.config!,\n bus,\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n getPersistentGoalApisForCommand: (routing) => this.persistentGoals.buildApisForRouting(routing),\n getWorkflowRunService: config.getWorkflowRunService,\n applySessionThinkingLevel: (sessionKey: string, level: ThinkLevel) => {\n this.agentManager.setThinkingLevel(sessionKey, level as ThinkingLevel);\n },\n getCurrentModel: () => this.agentOrchestrator.getCurrentModel(),\n switchModelForSession: (sessionKey: string, modelId: string) =>\n this.switchModelForSession(sessionKey, modelId),\n invalidateAgentSession: (sessionKey: string) => {\n this.agentManager.removeAgent(sessionKey);\n },\n resetSession: (sessionKey: string) => this.resetSession(sessionKey),\n abortSessionTurn: async (sessionKey: string) => {\n await this.streamManager.abort();\n this.agentOrchestrator.abort(sessionKey);\n },\n reloadSkills: () => this.refreshSkillsAfterDiskChange(),\n compactSession: (sessionKey, options) => this.sessionInspector.compact(sessionKey, options),\n btwQuery: (sessionKey, question) => this.sessionInspector.btwQuery(sessionKey, question),\n getSessionContextReport: (sessionKey, mode) => this.sessionInspector.report(sessionKey, mode),\n });\n\n this.sessionLifecycleManager = new SessionLifecycleManager(\n this.sessionStore,\n this.sessionTracker,\n this.lifecycleManager\n );\n\n this.persistentGoals = new PersistentGoalService({\n bus,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n sessionState: this.sessionState,\n getConfig: () => this.effectiveAppConfig(),\n getResolvedWorkspaceForSession: (sk) => this.agentManager.getResolvedWorkspaceForSession(sk),\n onSessionMetadataUpdated: this.onSessionMetadataUpdated,\n onGoalStatusUpdated: config.onGoalStatusUpdated,\n notifyWebchatTranscriptAppend: (sk, text) => this.turnDispatcher.notifyWebchatTranscriptAppend(sk, text),\n });\n\n this.sessionConfig = new SessionConfigService({\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n modelManager: this.modelManager,\n agentManager: this.agentManager,\n getConfig: () => this.effectiveAppConfig(),\n });\n\n this.sessionInspector = new SessionInspector({\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n modelManager: this.modelManager,\n agentManager: this.agentManager,\n sessionHydrator: this.sessionHydrator,\n getConfig: () => this.effectiveAppConfig(),\n getContextWindow: () => this.getContextWindow(),\n });\n\n this.outboundCoordinator = new OutboundCoordinator({\n bus,\n hookHandler: this.hookHandler,\n streamManager: this.streamManager,\n getConfig: () => this.effectiveAppConfig(),\n getLastAssistantPlainText: (sk) => this.getLastAssistantPlainText(sk),\n runPersistentGoalPostTurn: (payload) => this.persistentGoals.runPostTurn(payload),\n });\n\n this.turnDispatcher = new TurnDispatcher({\n log,\n agentManager: this.agentManager,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n sessionConfigStore: this.sessionConfigStore,\n sessionState: this.sessionState,\n commandHandler: this.commandHandler,\n getConfig: () => this.effectiveAppConfig(),\n requireConfig: () => {\n const c = this.config.config;\n if (!c) throw new Error('AgentService requires config.config');\n return c;\n },\n resolveSessionEndpoint: (sk) => this.resolveSessionEndpoint(sk),\n initSessionContext: (sk, channel, chatId) => this.initSessionContext(sk, channel, chatId),\n sessionHydrator: this.sessionHydrator,\n prepareInboundAttachments: (sk, att) => this.prepareInboundAttachments(sk, att),\n enqueueMaybeAutoTitleAfterPersist: (sk) => this.enqueueMaybeAutoTitleAfterPersist(sk),\n enqueueProvisionalSessionTitle: (sk, text) => this.enqueueProvisionalSessionTitle(sk, text),\n endDirectRequestContext: () => this.endDirectRequestContext(),\n onSessionTranscriptUpdated: this.onSessionTranscriptUpdated,\n resetSession: (sk) => this.resetSession(sk),\n sourceContextResolver: config.sourceContextResolver,\n });\n\n this.inboundLoop = new InboundLoop({\n log,\n agentId: this.agentId,\n bus,\n hookHandler: this.hookHandler,\n messageRouter: this.messageRouter,\n commandHandler: this.commandHandler,\n sessionContextManager: this.sessionContextManager,\n feedbackCoordinator: this.feedbackCoordinator,\n agentManager: this.agentManager,\n sessionLifecycleManager: this.sessionLifecycleManager,\n agentOrchestrator: this.agentOrchestrator,\n outboundCoordinator: this.outboundCoordinator,\n streamManager: this.streamManager,\n sessionState: this.sessionState,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n setupSessionEventHandling: (sk) => this.setupSessionEventHandling(sk),\n sessionHydrator: this.sessionHydrator,\n getLastAssistantPlainText: (sk) => this.getLastAssistantPlainText(sk),\n checkAndCompact: (sk, msgs) => this.checkAndCompact(sk, msgs),\n enqueueMaybeAutoTitleAfterPersist: (sk) => this.enqueueMaybeAutoTitleAfterPersist(sk),\n getConfig: () => this.effectiveAppConfig(),\n resetSession: (sk) => this.resetSession(sk),\n setStreamHandle: (handle) => this.setStreamHandle(handle),\n });\n\n // Register signal handlers only if not running as an Electron subprocess.\n // In Electron, the parent process manages the lifecycle and signals should not trigger disposal.\n const isElectronSubprocess = !!process.env.ELECTRON_RUN_AS_NODE;\n if (!isElectronSubprocess) {\n process.on('SIGINT', () => this.dispose());\n process.on('SIGTERM', () => this.dispose());\n }\n\n log.info('AgentService initialized');\n }\n\n private createSessionStore(): SessionStore {\n const windowConfig: Partial<WindowConfig> = {\n maxMessages: 100,\n keepRecentMessages: 20,\n preserveSystemMessages: true,\n };\n const compactionConfig: Partial<CompactionConfig> = {\n enabled: true,\n mode: 'abstractive',\n reserveTokens: 8000,\n triggerThreshold: 0.8,\n minMessagesBeforeCompact: 10,\n keepRecentMessages: 10,\n evictionWindow: 0.2,\n retentionWindow: 6,\n };\n const appCfg = this.config.config;\n if (!appCfg) {\n throw new Error('AgentService requires config.config for session store paths');\n }\n return new SessionStore(\n {\n config: appCfg,\n },\n windowConfig,\n compactionConfig,\n );\n }\n\n private createHookRunner(): ExtensionHookRunner | undefined {\n if (!this.config.extensionRegistry) return undefined;\n\n return new ExtensionHookRunner(this.config.extensionRegistry, {\n catchErrors: true,\n logger: {\n info: (msg: string) => log.info({ hook: true }, msg),\n warn: (msg: string) => log.warn({ hook: true }, msg),\n error: (msg: string) => log.error({ hook: true }, msg),\n },\n });\n }\n\n private createProgressManager(): ProgressFeedbackManager {\n return new ProgressFeedbackManager({\n level: 'normal',\n showThinking: true,\n streamToolProgress: true,\n heartbeatEnabled: true,\n heartbeatIntervalMs: 20000,\n longTaskThresholdMs: 30000,\n });\n }\n\n private initializeReliabilityModules(): void {\n this.errorTracker = new ToolErrorTracker({\n maxFailuresPerTool: 3,\n maxTotalFailures: 5,\n resetOnTurnEnd: true,\n });\n\n this.selfVerifyMiddleware = new SelfVerifyMiddleware({\n maxEditsPerFile: 5,\n enablePreCompletionCheck: true,\n minTurnsForVerification: 4,\n resetOnVerification: true,\n });\n\n this.requestLimiter = new RequestLimiter({\n maxRequestsPerTurn: 50,\n warnThreshold: 0.8,\n softLimit: false,\n });\n\n this.systemReminder = new SystemReminder({\n enabled: true,\n appendToToolResults: true,\n maxRemindersPerTurn: 3,\n });\n\n this.toolUsageAnalyzer = new ToolUsageAnalyzer({\n enabled: true,\n lowUsageThreshold: 5,\n veryLowUsageThreshold: 1,\n minCallsForAnalysis: 100,\n reportIntervalMs: 60 * 60 * 1000,\n });\n\n this.toolChainTracker = new ToolChainTracker({\n enabled: true,\n maxChainsPerSession: 10,\n maxNodesPerChain: 100,\n trackParams: true,\n trackResults: true,\n autoPrune: true,\n });\n\n this.errorPatternMatcher = new ErrorPatternMatcher({\n enabled: true,\n defaultMaxRetries: 1,\n logMatches: true,\n });\n\n // Initialize context middleware for automatic request tracking\n this.contextMiddleware = new ContextMiddleware();\n }\n\n private initializeLifecycleHandlers(): void {\n this.lifecycleManager.on('llm_response', new CompactionLifecycleHandler({\n minMessages: 20,\n maxTokens: 8000,\n preserveReasoning: true,\n accumulateUsage: true,\n }));\n\n log.debug(\n { handlers: this.lifecycleManager.getRegisteredHandlers() },\n 'Lifecycle handlers initialized'\n );\n }\n\n setChannelManager(channelManager: ChannelManager): void {\n this.modelManager.setChannelManager(channelManager);\n this.channelManagerRef = channelManager;\n this.inboundLoop.setChannelManager(channelManager);\n }\n\n /**\n * Apply config after save or hot reload so the default model updates without restarting the gateway.\n */\n applyAgentDefaultsFromConfig(config: Config): void {\n this.config.config = config;\n const ref = getAgentDefaultModelRef(config);\n this.config.model = ref;\n this.modelManager.updateFromConfig(config);\n this.agentManager.updateAgentDefaults(config);\n this.commandHandler.updateAgentConfig(config);\n }\n\n getSkillCatalog(): SkillCatalogEntry[] {\n return this.agentManager.getSkillCatalog();\n }\n\n getSkillCatalogSnapshot(): SkillCatalogSnapshot {\n return this.agentManager.getSkillCatalogSnapshot();\n }\n\n getAgentSkillAvailability(agentId: string): AgentSkillAvailabilityPayload {\n return this.agentManager.getAgentSkillAvailability(agentId);\n }\n\n getSkillMarkdownSource(skillName: string): SkillMarkdownPreviewPayload | null {\n return this.agentManager.getSkillMarkdownSource(skillName);\n }\n\n refreshSkillsAfterDiskChange(): void {\n this.agentManager.refreshSkillsAfterDiskChange();\n }\n\n refreshSkillsAfterSkillConfigChange(): void {\n this.agentManager.refreshSkillsAfterSkillConfigChange();\n }\n\n getModelForSession(sessionKey: string): string {\n return this.modelManager.getModelForSession(sessionKey);\n }\n\n async switchModelForSession(sessionKey: string, modelId: string): Promise<boolean> {\n const ok = await this.modelManager.switchModelForSession(sessionKey, modelId);\n if (!ok) return false;\n await this.sessionConfigStore.update(sessionKey, { modelOverride: modelId });\n const result = this.agentManager.setModelForSession(sessionKey, modelId);\n if (result) {\n this.sessionTracker.touchSession(sessionKey);\n }\n return true;\n }\n\n /**\n * Clears per-session model override so the next turn uses the configured agent default\n * (e.g. cron isolated job with no explicit model).\n */\n async resetSessionModelToAgentDefault(sessionKey: string): Promise<void> {\n await this.sessionConfig.clearModelOverride(sessionKey);\n }\n\n setStreamHandle(handle: StreamHandle): void {\n this.streamManager.setHandle(handle);\n this.feedbackCoordinator.setStreamHandle(handle);\n }\n\n clearStreamHandle(): void {\n this.streamManager.clearHandle();\n this.feedbackCoordinator.endTask();\n }\n\n /** Last assistant visible plain text for a session (e.g. after a webchat stream). */\n getLastAssistantPlainText(sessionKey: string): string {\n return (\n this.sessionState.getLastAssistantText(sessionKey) ??\n this.agentManager.getLastAssistantContent(sessionKey) ??\n ''\n );\n }\n\n beginInboundTurn(sessionKey: string): void {\n this.sessionState.beginInboundTurn(sessionKey);\n }\n\n endInboundTurn(sessionKey: string): void {\n this.sessionState.endInboundTurn(sessionKey);\n }\n\n getInboundTurnDepth(sessionKey: string): number {\n return this.sessionState.getInboundTurnDepth(sessionKey);\n }\n\n async start(): Promise<void> {\n await this.sessionConfigStore.initialize();\n await this.hookHandler.trigger('gateway_start', { port: 0, host: 'cli' });\n await this.reconcileDreamingCronJob().catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.warn({ err, errorMessage: em }, `Dreaming cron reconcile failed: ${em}`);\n });\n log.debug('Agent service started');\n void this.inboundLoop.start().catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.error({ err, errorMessage: em }, `Inbound loop failed: ${em}`);\n });\n }\n\n stop(): Promise<void> {\n this.inboundLoop.stop();\n this.agentManager.dispose();\n this.dispose();\n\n this.hookHandler.trigger('gateway_stop', { reason: 'stopped' });\n log.debug('Agent service stopped');\n return Promise.resolve();\n }\n\n /**\n * Reconcile managed Dreaming cron job against the current effective config.\n * Safe to call after config saves to apply changes without restarting the process.\n */\n async reconcileDreamingNow(): Promise<void> {\n await this.reconcileDreamingCronJob();\n }\n\n private async reconcileDreamingCronJob(): Promise<void> {\n const cron = this.config.getCronService?.();\n if (!cron) {\n return;\n }\n await reconcileManagedDreamingCronJobs(cron, this.effectiveAppConfig());\n }\n\n /**\n * Persist agent messages with the same sanitizer + transcript hygiene as AgentOrchestrator.\n * Uses persistence hygiene so `thinking` blocks remain on disk for the web UI (LLM load path still drops them).\n */\n private notifySessionTitleUpdated(sessionKey: string, name: string): void {\n this.onSessionMetadataUpdated?.(sessionKey, { name });\n }\n\n /** Fire-and-forget provisional title from first user text (webchat sidebar). */\n enqueueProvisionalSessionTitle(sessionKey: string, userText: string): void {\n void (async () => {\n try {\n await maybeSetProvisionalSessionTitle(\n this.sessionStore,\n sessionKey,\n userText,\n (sk, name) => this.notifySessionTitleUpdated(sk, name),\n );\n } catch (err) {\n log.warn({ err, sessionKey }, 'Provisional session title failed');\n }\n })();\n }\n\n /**\n * Fire-and-forget LLM refine after turn persist; skips user-locked and finalized LLM titles.\n */\n private enqueueMaybeAutoTitleAfterPersist(sessionKey: string): void {\n void (async () => {\n try {\n let modelRef =\n getAgentDefaultModelRef(this.config.config ?? ({} as Config)) ?? this.config.model;\n if (!modelRef?.trim()) {\n try {\n modelRef = this.modelManager.getModelForSession(sessionKey);\n } catch {\n modelRef = undefined;\n }\n }\n await maybeRefineSessionTitleWithLlm(\n this.sessionStore,\n sessionKey,\n modelRef?.trim() || undefined,\n (sk, name) => this.notifySessionTitleUpdated(sk, name),\n );\n } catch (err) {\n log.warn({ err, sessionKey }, 'Auto session title failed');\n }\n })();\n }\n\n private prepareLoadedSessionMessages(sessionKey: string, messages: AgentMessage[]): AgentMessage[] {\n let out = cleanTrailingErrors(messages);\n try {\n const model = this.modelManager.getResolvedModelForSession(sessionKey);\n out = tryApplySessionTranscriptHygiene(out, model);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Transcript hygiene on load skipped');\n }\n return out;\n }\n\n private async resolveSessionEndpoint(sessionKey: string): Promise<{ channel: string; chatId: string }> {\n const metadata = await this.sessionStore.getMetadata(sessionKey).catch(() => null);\n const channel = metadata?.routing?.source?.trim() || metadata?.sourceChannel?.trim() || 'cli';\n const chatId = metadata?.routing?.peerId?.trim() || metadata?.sourceChatId?.trim() || 'main';\n return { channel, chatId };\n }\n\n private initSessionContext(\n sessionKey: string,\n channel: string,\n chatId: string,\n senderId = '',\n ): SessionContext {\n const context: SessionContext = {\n sessionKey,\n channel,\n chatId,\n senderId,\n isGroup: false,\n };\n\n this.contextMiddleware.onRequest({\n sessionKey,\n userId: context.senderId,\n channel,\n chatId,\n });\n\n // Direct turn entry points (one-shot + streaming generator) cannot wrap their\n // body in `sessionContextManager.runWith(ctx, fn)` cleanly — the streaming\n // path is an async generator, and both flows already use a try/finally for\n // side-effect cleanup. We use `enter` so the context is visible via ALS for\n // every async resource launched after this call returns. The context is\n // overwritten or cleared by the next direct turn (each direct turn calls\n // `initSessionContext` first), so there is no cross-session leak in practice.\n this.sessionContextManager.enter(context);\n this.feedbackCoordinator.setContext(context);\n this.agentManager.getOrCreateAgent(sessionKey);\n this.setupSessionEventHandling(sessionKey);\n\n return context;\n }\n\n /**\n * Persist inbound file attachments to the global media store.\n */\n async prepareInboundAttachments(\n _sessionKey: string,\n attachments?: InboundAttachmentInput[],\n ): Promise<MediaRef[] | undefined> {\n return persistInboundAttachments(attachments, {\n resolveUri: async (uri) => {\n const target = parseNoteAttachmentTarget(uri);\n if (!target) return null;\n const notesService = (this as unknown as {\n notesServiceInstance?: {\n getAttachmentPath(noteId: string, attachmentId: string): Promise<{\n filePath: string;\n mimeType?: string;\n fileName?: string;\n } | null>;\n };\n }).notesServiceInstance;\n if (!notesService) return null;\n const located = await notesService.getAttachmentPath(target.noteId, target.attachmentId);\n if (!located) return null;\n const buffer = await readFile(located.filePath);\n return {\n buffer,\n path: located.filePath,\n mimeType: located.mimeType,\n name: located.fileName,\n size: buffer.byteLength,\n };\n },\n });\n }\n\n private endDirectRequestContext(): void {\n // `sessionContextManager` is ALS-backed: the context for the current async\n // chain drops automatically when the chain unwinds. The feedback\n // coordinator + context middleware still use singleton state, so clear\n // them explicitly here.\n this.feedbackCoordinator.clearContext();\n this.contextMiddleware.onResponse();\n }\n\n /**\n * Reset a session's transcript and drop the in-memory agent so the next turn\n * reloads from disk. Combines two collaborators (sessionStore + agentManager)\n * so it stays on `AgentService`; pure sessionStore reads should use\n * `agentService.sessionStore.*` directly.\n */\n async clearSessionMessages(key: string): Promise<void> {\n await this.sessionStore.saveMessages(key, []);\n this.agentManager.removeAgent(key);\n }\n\n /**\n * Reset session transcript (archive + new session id) and evict in-memory agent state.\n * Preserves the session key and persisted per-session overrides.\n */\n async resetSession(\n key: string,\n ): Promise<{ sessionId: string; previousSessionId: string } | null> {\n const { abortEmbeddedRun } = await import('./embedded/runs.js');\n const { retireSessionMcpRuntimeForSessionKey } = await import('./mcp/bundle-mcp-tools.js');\n await abortEmbeddedRun(key);\n const outcome = await this.sessionStore.reset(key);\n if (!outcome) {\n return null;\n }\n this.agentManager.removeAgent(key);\n await retireSessionMcpRuntimeForSessionKey({ sessionKey: key, reason: 'session-reset' });\n return outcome;\n }\n\n /**\n * Drop in-memory agent so the next turn reloads transcript from disk (e.g. after checkpoint restore).\n */\n evictSessionAgent(sessionKey: string): void {\n this.agentManager.removeAgent(sessionKey);\n }\n\n /**\n * Load session working directory override into AgentManager, ensure directory exists.\n * Call before AgentManager.getOrCreateAgent for this session.\n */\n /** Workspace root for UI file tree / editor (same as agent tools after hydration). */\n async getEffectiveWorkspacePathForSession(sessionKey: string): Promise<string> {\n await this.sessionHydrator.workspace(sessionKey);\n const cfg = this.config.config!;\n const sc = await this.sessionConfigStore.get(sessionKey);\n return effectiveWorkspacePathForSession(cfg, sessionKey, sc);\n }\n\n /**\n * Best-effort timezone resolution for webchat envelope timestamps.\n * Reads `USER.md` under the agent `profile/` directory and extracts a `Timezone:` line.\n */\n resolveUserTimezoneForSession(sessionKey: string): string | undefined {\n try {\n const cfg = this.effectiveAppConfig();\n if (!cfg) return undefined;\n const { agentId } = resolveEffectiveAgentProfileForSession(cfg, sessionKey);\n const userPath = join(resolveAgentProfileDir(cfg, agentId), 'USER.md');\n if (!existsSync(userPath)) return undefined;\n const raw = readFileSync(userPath, 'utf-8');\n const match = raw.match(/Timezone:\\s*(.+)/i);\n const tz = match?.[1]?.trim();\n return tz || undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Setup event handling for a specific session\n */\n private setupSessionEventHandling(sessionKey: string): void {\n if (this.sessionState.hasSessionEventUnsubscriber(sessionKey)) {\n return;\n }\n\n const unsubscribe = this.agentManager.subscribeToSession(sessionKey, (event) => {\n this.handleSessionEvent(sessionKey, event);\n });\n\n if (unsubscribe) {\n this.sessionState.setSessionEventUnsubscriber(sessionKey, unsubscribe);\n }\n }\n\n /**\n * Handle events from a specific session's agent\n */\n private handleSessionEvent(sessionKey: string, event: AgentEvent): void {\n const currentContext = this.sessionContextManager.getContext();\n if (!currentContext) {\n // Inbound `finally` clears context before trailing agent `message_update` events finish — ignore (not a bug).\n return;\n }\n\n if (currentContext.sessionKey !== sessionKey) {\n // Event from a different session — still process with current context where applicable\n this.agentEventHandler.handle(event, currentContext);\n return;\n }\n\n // Handle streaming updates for the current session\n if (event.type === 'message_update') {\n const msgEvent = event as Extract<AgentEvent, { type: 'message_update' }>;\n if (msgEvent.message?.role === 'assistant') {\n const content = msgEvent.message.content;\n const text = Array.isArray(content)\n ? extractTextContent(content as Array<{ type: string; text?: string }>)\n : String(content);\n\n this.streamManager.update(text);\n }\n }\n\n this.agentEventHandler.handle(event, currentContext);\n }\n\n private async checkAndCompact(sessionKey: string, messages: AgentMessage[]): Promise<void> {\n const contextWindow = this.getContextWindow();\n const prep = this.sessionStore.prepareCompaction(sessionKey, messages, contextWindow);\n if (!prep.needsCompaction) return;\n\n log.info({ sessionKey, reason: prep.stats?.reason, usagePercent: prep.stats?.usagePercent }, 'Session needs compaction');\n\n const result = await this.sessionStore.compact(sessionKey, messages, contextWindow, undefined, false);\n await this.hookHandler.trigger('after_compaction', {\n messageCount: messages.length,\n tokenCount: result.tokensBefore,\n compactedCount: messages.length - result.firstKeptIndex,\n });\n log.info({ sessionKey, tokensBefore: result.tokensBefore, tokensAfter: result.tokensAfter }, 'Session compacted');\n\n await this.checkAndFlush(sessionKey, messages, result, contextWindow);\n }\n\n private resolveMemoryFlushConfig(): MemoryFlushConfig {\n return {\n enabled: true,\n threshold: 0.88,\n softThresholdTokens: 4000,\n afterCompactions: 2,\n maxEntryChars: 2000,\n includeToolHistory: true,\n };\n }\n\n private async checkAndFlush(\n sessionKey: string,\n messages: AgentMessage[],\n compactionResult: import('./memory/compaction.js').CompactionResult,\n contextWindow: number,\n ): Promise<void> {\n const cfg = this.resolveMemoryFlushConfig();\n if (!cfg.enabled) return;\n\n const metadata = await this.sessionStore.getMetadata(sessionKey);\n const compactedCount = metadata?.compactedCount ?? 0;\n\n const totalTokens = await this.sessionStore.estimateTokenUsage(sessionKey, messages);\n const usagePercent = totalTokens / contextWindow;\n const effectiveThreshold = Math.max(0.5, cfg.threshold - cfg.softThresholdTokens / contextWindow);\n const exceedsThreshold = usagePercent >= effectiveThreshold;\n\n const tooManyCompactions = compactedCount >= cfg.afterCompactions;\n\n if (!exceedsThreshold && !tooManyCompactions) return;\n\n const workspaceDir = this.agentManager.getResolvedWorkspaceForSession(sessionKey);\n const flushResult = await this.memoryFlushService.flush({\n workspaceDir,\n sessionKey,\n compactionResult,\n config: cfg,\n });\n\n if (flushResult.flushed) {\n log.info({ sessionKey, entryPath: flushResult.entryPath, entryLength: flushResult.entryLength }, 'Memory flushed to daily notes');\n const currentMeta = await this.sessionStore.getMetadata(sessionKey);\n await this.sessionStore.updateMetadata(sessionKey, {\n lastFlushedAt: new Date().toISOString(),\n flushCount: (currentMeta?.flushCount ?? 0) + 1,\n });\n }\n }\n\n private getContextWindow(): number {\n return 128000;\n }\n\n private dispose(): void {\n this.sessionTracker.dispose();\n this.sessionState.disposeAll();\n this.agentManager.dispose();\n this.workflowProgressBrokerHandle?.dispose();\n this.workflowProgressBrokerHandle = null;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"service.js","names":[],"sources":["../../../src/agent/service.ts"],"sourcesContent":["import type { AgentEvent, AgentMessage, ThinkingLevel } from '@earendil-works/pi-agent-core';\nimport type { MessageBus } from '../infra/bus/index.js';\nimport { type Config, getAgentDefaultModelRef } from '../config/schema.js';\nimport {\n maybeRefineSessionTitleWithLlm,\n maybeSetProvisionalSessionTitle,\n} from '../session/session-title.js';\nimport type { ChannelManager } from '../channels/manager.js';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\n\nimport {\n SessionStore,\n SessionConfigStore,\n onSessionTranscriptUpdate,\n effectiveWorkspacePathForSession,\n type CompactionConfig,\n type WindowConfig,\n} from '../session/index.js';\nimport { type ThinkLevel } from './transcript/thinking-types.js';\nimport { createLogger } from '../utils/logger.js';\nimport { ExtensionHookRunner } from '../extensions/index.js';\nimport { extractTextContent } from './context/workspace.js';\nimport { SessionTracker } from './session/tracker.js';\nimport { ModelManager } from './models/index.js';\nimport { initializeCommands } from '../chat-commands/index.js';\nimport { ProgressFeedbackManager } from './lifecycle/progress.js';\nimport { HookHandler } from './lifecycle/hook-handler.js';\nimport { ToolErrorTracker } from './tools/error-tracker.js';\nimport { RequestLimiter } from './models/request-limiter.js';\nimport { SystemReminder } from './prompt/system-reminder.js';\nimport { ToolUsageAnalyzer } from './tools/usage-analyzer.js';\nimport { ToolChainTracker } from './tools/chain-tracker.js';\nimport { ErrorPatternMatcher } from './tools/error-pattern-matcher.js';\nimport { ContextMiddleware, SelfVerifyMiddleware } from './middleware/index.js';\nimport { LifecycleManager } from './lifecycle/index.js';\nimport { CompactionLifecycleHandler } from './lifecycle/handlers/compaction.js';\n\nimport {\n MessageRouter,\n CommandHandler,\n StreamManager,\n OutboundCoordinator,\n} from './messaging/index.js';\nimport { InboundLoop } from './inbound/inbound-loop.js';\nimport { TurnDispatcher } from './inbound/turn-dispatcher.js';\nimport {\n SessionContextManager,\n SessionLifecycleManager,\n SessionStateBag,\n SessionConfigService,\n SessionHydrator,\n SessionInspector,\n type SessionContext,\n} from './session/index.js';\nimport { AgentOrchestrator, AgentEventHandler } from './orchestration/index.js';\nimport {\n getWorkflowProgressBroker,\n type BrokerListenerHandle,\n} from './workflow/index.js';\nimport { FeedbackCoordinator } from './feedback/index.js';\nimport {\n AgentManager,\n type AgentSkillAvailabilityPayload,\n type SkillCatalogEntry,\n type SkillCatalogSnapshot,\n} from './agent-manager.js';\nimport type { SkillMarkdownPreviewPayload } from './skills/types.js';\nimport type { AgentServiceConfig, StreamHandle } from './service.types.js';\nimport { PersistentGoalService } from './goals/persistent-goal-service.js';\nimport { reconcileManagedDreamingCronJobs } from './service/reconcile-dreaming-cron.js';\nimport { parseNoteAttachmentTarget } from '../notes/attachment-ref.js';\n\nimport {\n resolveAgentHomeDir,\n resolveDefaultAgentId,\n} from './agent-scope.js';\nimport {\n extractProfileAgentId,\n} from '../config/agent-profile.js';\nimport { resolveUserProfilePath } from '../config/paths.js';\nimport { cleanTrailingErrors } from './memory/message-sanitizer.js';\nimport { MemoryFlushService } from './memory/memory-flush.js';\nimport type { MemoryFlushConfig } from './memory/memory-flush.js';\nimport { tryApplySessionTranscriptHygiene } from './transcript/transcript-hygiene.js';\nimport {\n persistInboundAttachments,\n type InboundAttachmentInput,\n type MediaRef,\n} from '../channels/attachments/inbound-persist.js';\nimport { applyConfigOverrides } from '../config/runtime-overrides.js';\n\nexport type { AgentServiceConfig, AgentContext, StreamHandle } from './service.types.js';\n\nconst log = createLogger('AgentService');\n\nfunction assistantMessageText(message: AgentMessage): string {\n const content = (message as { content?: unknown }).content;\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n return content\n .map((block) => {\n if (!block || typeof block !== 'object') return '';\n const rec = block as { type?: unknown; text?: unknown };\n return rec.type === 'text' && typeof rec.text === 'string' ? rec.text : '';\n })\n .join('');\n}\n\nexport class AgentService {\n /**\n * Persistent transcript + session-metadata store. Public so the gateway/TUI\n * can read sessions, delete them, etc. without forcing every CRUD-style\n * operation through a delegation method on `AgentService`.\n */\n readonly sessionStore: SessionStore;\n private sessionConfigStore: SessionConfigStore;\n private hookRunner?: ExtensionHookRunner;\n private agentId: string;\n private workspaceDir: string;\n private channelManagerRef: ChannelManager | null = null;\n private bus: MessageBus;\n private config: AgentServiceConfig;\n private sessionTracker: SessionTracker;\n private modelManager: ModelManager;\n private progressManager: ProgressFeedbackManager;\n private hookHandler: HookHandler;\n private lifecycleManager: LifecycleManager;\n private errorTracker: ToolErrorTracker;\n private requestLimiter: RequestLimiter;\n private systemReminder: SystemReminder;\n private toolUsageAnalyzer: ToolUsageAnalyzer;\n private toolChainTracker: ToolChainTracker;\n private errorPatternMatcher: ErrorPatternMatcher;\n private selfVerifyMiddleware: SelfVerifyMiddleware;\n private contextMiddleware: ContextMiddleware;\n\n private messageRouter: MessageRouter;\n private commandHandler: CommandHandler;\n private streamManager: StreamManager;\n /**\n * Outbound pipeline: typing controller, silence guard, final response publish,\n * extension `message_sending`/`message_sent` hooks, post-turn `webchat_turn_complete`\n * event. Public so the gateway / channels can drive it directly.\n */\n readonly outboundCoordinator: OutboundCoordinator;\n private inboundLoop: InboundLoop;\n /**\n * Direct-turn entry points: `processDirect` (one-shot), `processDirectStreaming`\n * (SSE generator), webchat steering and SSE injection. Public so the gateway,\n * TUI, CLI, and cron jobs do not need to thread every call through `AgentService`.\n */\n readonly turnDispatcher: TurnDispatcher;\n /**\n * `/goal` runtime: continuation scheduling, persistent-goal API factory,\n * stream-outcome state, post-turn verdict. Public so the gateway can wire\n * the webchat continuation scheduler and read stream outcomes directly.\n */\n readonly persistentGoals: PersistentGoalService;\n /**\n * Per-session config writes (model / thinking / reasoning / working directory).\n * Public so REST endpoints and CLI flows can hit it without going through a\n * monolithic patch entrypoint on `AgentService`.\n */\n readonly sessionConfig: SessionConfigService;\n /**\n * Hydration — read persisted per-session config and apply it to the runtime\n * (AgentManager / ModelManager). The mirror image of `sessionConfig`: writes\n * go through `sessionConfig`, reads-into-runtime go through `sessionHydrator`.\n */\n readonly sessionHydrator: SessionHydrator;\n /**\n * Read-only introspection (compaction, /context report, /btw, contextUsage,\n * agentConfig view). Public so REST endpoints and CLI flows can query a\n * session's view without going through delegating methods on `AgentService`.\n */\n readonly sessionInspector: SessionInspector;\n private sessionContextManager: SessionContextManager;\n private sessionLifecycleManager: SessionLifecycleManager;\n private agentOrchestrator: AgentOrchestrator;\n private agentEventHandler: AgentEventHandler;\n private workflowProgressBrokerHandle: BrokerListenerHandle | null = null;\n private feedbackCoordinator: FeedbackCoordinator;\n private agentManager: AgentManager;\n private memoryFlushService: MemoryFlushService;\n\n /**\n * Unified per-session state container (replaces six ad-hoc Maps). Owns webchat\n * publishers, last assistant text, embedded stream buffer, persistent-goal stream\n * outcomes, concurrent-turn depth, and event-listener unsubscribers; runs a TTL\n * sweep for slots that have no explicit owner.\n */\n private sessionState = new SessionStateBag();\n\n /** Gateway: notify UI after direct `SessionStore.updateMetadata` (no SessionManager emit). */\n private onSessionMetadataUpdated?: (sessionKey: string, patch?: { name?: string }) => void;\n private onSessionTranscriptUpdated?: (sessionKey: string) => void;\n\n private effectiveAppConfig(): Config | undefined {\n const base = this.config.config;\n return base ? applyConfigOverrides(base) : undefined;\n }\n\n constructor(bus: MessageBus, config: AgentServiceConfig) {\n this.bus = bus;\n this.config = config;\n this.onSessionMetadataUpdated = config.onSessionMetadataUpdated;\n this.onSessionTranscriptUpdated = config.onSessionTranscriptUpdated;\n this.agentId = `agent-${Date.now()}`;\n this.workspaceDir = config.workspace;\n\n this.sessionTracker = new SessionTracker();\n this.modelManager = new ModelManager({\n defaultModel: config.model,\n config: config.config,\n });\n\n initializeCommands();\n log.debug('Command system initialized');\n\n this.sessionStore = config.sessionStore ?? this.createSessionStore();\n onSessionTranscriptUpdate((update) => {\n void this.sessionStore.syncEmbeddedTranscriptUpdate(update).catch((err) => {\n log.warn(\n { err, sessionKey: update.sessionKey },\n 'Transcript index sync failed',\n );\n });\n const sk = update.sessionKey?.trim();\n if (sk) {\n this.onSessionTranscriptUpdated?.(sk);\n }\n });\n const appCfgForPaths = this.config.config;\n if (!appCfgForPaths) {\n throw new Error('AgentService requires config.config for session paths');\n }\n const defaultAid = resolveDefaultAgentId(appCfgForPaths);\n const defaultAgentHome = resolveAgentHomeDir(appCfgForPaths, defaultAid);\n this.sessionConfigStore = new SessionConfigStore(defaultAgentHome);\n\n this.hookRunner = this.createHookRunner();\n this.hookHandler = new HookHandler({\n hookRunner: this.hookRunner,\n agentId: this.agentId,\n get sessionKey() { return this.currentContext?.sessionKey; },\n });\n\n this.progressManager = this.createProgressManager();\n this.initializeReliabilityModules();\n\n this.lifecycleManager = new LifecycleManager();\n this.initializeLifecycleHandlers();\n\n this.streamManager = new StreamManager();\n this.sessionContextManager = new SessionContextManager();\n this.feedbackCoordinator = new FeedbackCoordinator({\n progressManager: this.progressManager,\n bus,\n });\n\n // Initialize AgentManager\n this.memoryFlushService = new MemoryFlushService();\n\n this.agentManager = new AgentManager({\n workspace: config.workspace,\n model: config.model,\n config: config.config,\n extensionRegistry: config.extensionRegistry,\n hookRunner: this.hookRunner,\n bus,\n getCurrentContext: () => this.sessionContextManager.getContext(),\n getSessionStore: () => this.sessionStore,\n getModelManager: () => this.modelManager,\n thinkingLevel: config.thinkingLevel,\n reasoningLevel: config.reasoningLevel,\n verboseLevel: config.verboseLevel,\n gatewayClarify: config.gatewayClarify,\n getCronService: config.getCronService,\n getWorkflowRunService: config.getWorkflowRunService,\n onSkillsUpdated: config.onSkillsUpdated,\n isWorkspaceTrusted: config.isWorkspaceTrusted,\n });\n\n this.agentEventHandler = new AgentEventHandler({\n progressManager: this.progressManager,\n errorTracker: this.errorTracker,\n requestLimiter: this.requestLimiter,\n lifecycleManager: this.lifecycleManager,\n toolChainTracker: this.toolChainTracker,\n selfVerifyMiddleware: this.selfVerifyMiddleware,\n systemReminder: this.systemReminder,\n toolUsageAnalyzer: this.toolUsageAnalyzer,\n errorPatternMatcher: this.errorPatternMatcher,\n });\n\n // Wire the workflow progress broker into the session event bus. Channel\n // extensions self-register their capability against the singleton at\n // boot; the broker only forwards updates to whatever has registered, so\n // this is safe even when no channel is configured.\n this.workflowProgressBrokerHandle = getWorkflowProgressBroker().attachTo(\n this.agentEventHandler,\n );\n\n // sessionHydrator is constructed early because AgentOrchestrator + InboundLoop +\n // TurnDispatcher all need it; the SessionConfigService instance below also\n // shares the same constructor parameters.\n this.sessionHydrator = new SessionHydrator({\n sessionConfigStore: this.sessionConfigStore,\n agentManager: this.agentManager,\n modelManager: this.modelManager,\n getConfig: () => this.effectiveAppConfig(),\n });\n\n this.agentOrchestrator = new AgentOrchestrator({\n agentManager: this.agentManager,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n eventHandler: this.agentEventHandler,\n feedbackCoordinator: this.feedbackCoordinator,\n sessionConfigStore: this.sessionConfigStore,\n sessionHydrator: this.sessionHydrator,\n getConfig: () => this.effectiveAppConfig(),\n getThinkingDefault: () => undefined,\n getThinkingDefaultForSession: (sessionKey: string) =>\n this.agentManager.getThinkingDefaultForSession(sessionKey),\n workspaceRoot: this.workspaceDir,\n getWorkspaceRootForSession: (sessionKey: string) =>\n this.agentManager.getResolvedWorkspaceForSession(sessionKey),\n getAgentInternalStorageRootForSession: (sessionKey: string) =>\n resolveAgentHomeDir(this.config.config!, extractProfileAgentId(sessionKey, this.config.config!)),\n enqueueAutoTitle: (sessionKey: string) => this.enqueueMaybeAutoTitleAfterPersist(sessionKey),\n onEmbeddedStreamEvent: (sessionKey, event) => {\n const ctx = this.sessionContextManager.getContext();\n if (!ctx || ctx.sessionKey !== sessionKey) {\n return;\n }\n if (event.type === 'message_update' && event.message.role === 'assistant') {\n const next = assistantMessageText(event.message);\n this.streamManager.update(next);\n }\n },\n onEmbeddedTurnComplete: (sessionKey, text) => {\n if (text) {\n this.sessionState.setLastAssistantText(sessionKey, text);\n }\n this.sessionState.clearEmbeddedStreamText(sessionKey);\n },\n });\n\n this.messageRouter = new MessageRouter();\n this.commandHandler = new CommandHandler({\n config: config.config!,\n bus,\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n getPersistentGoalApisForCommand: (routing) => this.persistentGoals.buildApisForRouting(routing),\n getWorkflowRunService: config.getWorkflowRunService,\n applySessionThinkingLevel: (sessionKey: string, level: ThinkLevel) => {\n this.agentManager.setThinkingLevel(sessionKey, level as ThinkingLevel);\n },\n getCurrentModel: () => this.agentOrchestrator.getCurrentModel(),\n switchModelForSession: (sessionKey: string, modelId: string) =>\n this.switchModelForSession(sessionKey, modelId),\n invalidateAgentSession: (sessionKey: string) => {\n this.agentManager.removeAgent(sessionKey);\n },\n resetSession: (sessionKey: string) => this.resetSession(sessionKey),\n abortSessionTurn: async (sessionKey: string) => {\n await this.streamManager.abort();\n this.agentOrchestrator.abort(sessionKey);\n },\n reloadSkills: () => this.refreshSkillsAfterDiskChange(),\n compactSession: (sessionKey, options) => this.sessionInspector.compact(sessionKey, options),\n btwQuery: (sessionKey, question) => this.sessionInspector.btwQuery(sessionKey, question),\n getSessionContextReport: (sessionKey, mode) => this.sessionInspector.report(sessionKey, mode),\n });\n\n this.sessionLifecycleManager = new SessionLifecycleManager(\n this.sessionStore,\n this.sessionTracker,\n this.lifecycleManager\n );\n\n this.persistentGoals = new PersistentGoalService({\n bus,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n sessionState: this.sessionState,\n getConfig: () => this.effectiveAppConfig(),\n getResolvedWorkspaceForSession: (sk) => this.agentManager.getResolvedWorkspaceForSession(sk),\n onSessionMetadataUpdated: this.onSessionMetadataUpdated,\n onGoalStatusUpdated: config.onGoalStatusUpdated,\n notifyWebchatTranscriptAppend: (sk, text) => this.turnDispatcher.notifyWebchatTranscriptAppend(sk, text),\n });\n\n this.sessionConfig = new SessionConfigService({\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n modelManager: this.modelManager,\n agentManager: this.agentManager,\n getConfig: () => this.effectiveAppConfig(),\n });\n\n this.sessionInspector = new SessionInspector({\n sessionStore: this.sessionStore,\n sessionConfigStore: this.sessionConfigStore,\n modelManager: this.modelManager,\n agentManager: this.agentManager,\n sessionHydrator: this.sessionHydrator,\n getConfig: () => this.effectiveAppConfig(),\n getContextWindow: () => this.getContextWindow(),\n });\n\n this.outboundCoordinator = new OutboundCoordinator({\n bus,\n hookHandler: this.hookHandler,\n streamManager: this.streamManager,\n getConfig: () => this.effectiveAppConfig(),\n getLastAssistantPlainText: (sk) => this.getLastAssistantPlainText(sk),\n runPersistentGoalPostTurn: (payload) => this.persistentGoals.runPostTurn(payload),\n });\n\n this.turnDispatcher = new TurnDispatcher({\n log,\n agentManager: this.agentManager,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n sessionConfigStore: this.sessionConfigStore,\n sessionState: this.sessionState,\n commandHandler: this.commandHandler,\n getConfig: () => this.effectiveAppConfig(),\n requireConfig: () => {\n const c = this.config.config;\n if (!c) throw new Error('AgentService requires config.config');\n return c;\n },\n resolveSessionEndpoint: (sk) => this.resolveSessionEndpoint(sk),\n initSessionContext: (sk, channel, chatId) => this.initSessionContext(sk, channel, chatId),\n sessionHydrator: this.sessionHydrator,\n prepareInboundAttachments: (sk, att) => this.prepareInboundAttachments(sk, att),\n enqueueMaybeAutoTitleAfterPersist: (sk) => this.enqueueMaybeAutoTitleAfterPersist(sk),\n enqueueProvisionalSessionTitle: (sk, text) => this.enqueueProvisionalSessionTitle(sk, text),\n endDirectRequestContext: () => this.endDirectRequestContext(),\n onSessionTranscriptUpdated: this.onSessionTranscriptUpdated,\n resetSession: (sk) => this.resetSession(sk),\n sourceContextResolver: config.sourceContextResolver,\n });\n\n this.inboundLoop = new InboundLoop({\n log,\n agentId: this.agentId,\n bus,\n hookHandler: this.hookHandler,\n messageRouter: this.messageRouter,\n commandHandler: this.commandHandler,\n sessionContextManager: this.sessionContextManager,\n feedbackCoordinator: this.feedbackCoordinator,\n agentManager: this.agentManager,\n sessionLifecycleManager: this.sessionLifecycleManager,\n agentOrchestrator: this.agentOrchestrator,\n outboundCoordinator: this.outboundCoordinator,\n streamManager: this.streamManager,\n sessionState: this.sessionState,\n sessionStore: this.sessionStore,\n modelManager: this.modelManager,\n setupSessionEventHandling: (sk) => this.setupSessionEventHandling(sk),\n sessionHydrator: this.sessionHydrator,\n getLastAssistantPlainText: (sk) => this.getLastAssistantPlainText(sk),\n checkAndCompact: (sk, msgs) => this.checkAndCompact(sk, msgs),\n enqueueMaybeAutoTitleAfterPersist: (sk) => this.enqueueMaybeAutoTitleAfterPersist(sk),\n getConfig: () => this.effectiveAppConfig(),\n resetSession: (sk) => this.resetSession(sk),\n setStreamHandle: (handle) => this.setStreamHandle(handle),\n });\n\n // Register signal handlers only if not running as an Electron subprocess.\n // In Electron, the parent process manages the lifecycle and signals should not trigger disposal.\n const isElectronSubprocess = !!process.env.ELECTRON_RUN_AS_NODE;\n if (!isElectronSubprocess) {\n process.on('SIGINT', () => this.dispose());\n process.on('SIGTERM', () => this.dispose());\n }\n\n log.info('AgentService initialized');\n }\n\n private createSessionStore(): SessionStore {\n const windowConfig: Partial<WindowConfig> = {\n maxMessages: 100,\n keepRecentMessages: 20,\n preserveSystemMessages: true,\n };\n const compactionConfig: Partial<CompactionConfig> = {\n enabled: true,\n mode: 'abstractive',\n reserveTokens: 8000,\n triggerThreshold: 0.8,\n minMessagesBeforeCompact: 10,\n keepRecentMessages: 10,\n evictionWindow: 0.2,\n retentionWindow: 6,\n };\n const appCfg = this.config.config;\n if (!appCfg) {\n throw new Error('AgentService requires config.config for session store paths');\n }\n return new SessionStore(\n {\n config: appCfg,\n },\n windowConfig,\n compactionConfig,\n );\n }\n\n private createHookRunner(): ExtensionHookRunner | undefined {\n if (!this.config.extensionRegistry) return undefined;\n\n return new ExtensionHookRunner(this.config.extensionRegistry, {\n catchErrors: true,\n logger: {\n info: (msg: string) => log.info({ hook: true }, msg),\n warn: (msg: string) => log.warn({ hook: true }, msg),\n error: (msg: string) => log.error({ hook: true }, msg),\n },\n });\n }\n\n private createProgressManager(): ProgressFeedbackManager {\n return new ProgressFeedbackManager({\n level: 'normal',\n showThinking: true,\n streamToolProgress: true,\n heartbeatEnabled: true,\n heartbeatIntervalMs: 20000,\n longTaskThresholdMs: 30000,\n });\n }\n\n private initializeReliabilityModules(): void {\n this.errorTracker = new ToolErrorTracker({\n maxFailuresPerTool: 3,\n maxTotalFailures: 5,\n resetOnTurnEnd: true,\n });\n\n this.selfVerifyMiddleware = new SelfVerifyMiddleware({\n maxEditsPerFile: 5,\n enablePreCompletionCheck: true,\n minTurnsForVerification: 4,\n resetOnVerification: true,\n });\n\n this.requestLimiter = new RequestLimiter({\n maxRequestsPerTurn: 50,\n warnThreshold: 0.8,\n softLimit: false,\n });\n\n this.systemReminder = new SystemReminder({\n enabled: true,\n appendToToolResults: true,\n maxRemindersPerTurn: 3,\n });\n\n this.toolUsageAnalyzer = new ToolUsageAnalyzer({\n enabled: true,\n lowUsageThreshold: 5,\n veryLowUsageThreshold: 1,\n minCallsForAnalysis: 100,\n reportIntervalMs: 60 * 60 * 1000,\n });\n\n this.toolChainTracker = new ToolChainTracker({\n enabled: true,\n maxChainsPerSession: 10,\n maxNodesPerChain: 100,\n trackParams: true,\n trackResults: true,\n autoPrune: true,\n });\n\n this.errorPatternMatcher = new ErrorPatternMatcher({\n enabled: true,\n defaultMaxRetries: 1,\n logMatches: true,\n });\n\n // Initialize context middleware for automatic request tracking\n this.contextMiddleware = new ContextMiddleware();\n }\n\n private initializeLifecycleHandlers(): void {\n this.lifecycleManager.on('llm_response', new CompactionLifecycleHandler({\n minMessages: 20,\n maxTokens: 8000,\n preserveReasoning: true,\n accumulateUsage: true,\n }));\n\n log.debug(\n { handlers: this.lifecycleManager.getRegisteredHandlers() },\n 'Lifecycle handlers initialized'\n );\n }\n\n setChannelManager(channelManager: ChannelManager): void {\n this.modelManager.setChannelManager(channelManager);\n this.channelManagerRef = channelManager;\n this.inboundLoop.setChannelManager(channelManager);\n }\n\n /**\n * Apply config after save or hot reload so the default model updates without restarting the gateway.\n */\n applyAgentDefaultsFromConfig(config: Config): void {\n this.config.config = config;\n const ref = getAgentDefaultModelRef(config);\n this.config.model = ref;\n this.modelManager.updateFromConfig(config);\n this.agentManager.updateAgentDefaults(config);\n this.commandHandler.updateAgentConfig(config);\n }\n\n getSkillCatalog(): SkillCatalogEntry[] {\n return this.agentManager.getSkillCatalog();\n }\n\n getSkillCatalogSnapshot(): SkillCatalogSnapshot {\n return this.agentManager.getSkillCatalogSnapshot();\n }\n\n getAgentSkillAvailability(agentId: string): AgentSkillAvailabilityPayload {\n return this.agentManager.getAgentSkillAvailability(agentId);\n }\n\n getSkillMarkdownSource(skillName: string): SkillMarkdownPreviewPayload | null {\n return this.agentManager.getSkillMarkdownSource(skillName);\n }\n\n refreshSkillsAfterDiskChange(): void {\n this.agentManager.refreshSkillsAfterDiskChange();\n }\n\n refreshSkillsAfterSkillConfigChange(): void {\n this.agentManager.refreshSkillsAfterSkillConfigChange();\n }\n\n getModelForSession(sessionKey: string): string {\n return this.modelManager.getModelForSession(sessionKey);\n }\n\n async switchModelForSession(sessionKey: string, modelId: string): Promise<boolean> {\n const ok = await this.modelManager.switchModelForSession(sessionKey, modelId);\n if (!ok) return false;\n await this.sessionConfigStore.update(sessionKey, { modelOverride: modelId });\n const result = this.agentManager.setModelForSession(sessionKey, modelId);\n if (result) {\n this.sessionTracker.touchSession(sessionKey);\n }\n return true;\n }\n\n /**\n * Clears per-session model override so the next turn uses the configured agent default\n * (e.g. cron isolated job with no explicit model).\n */\n async resetSessionModelToAgentDefault(sessionKey: string): Promise<void> {\n await this.sessionConfig.clearModelOverride(sessionKey);\n }\n\n setStreamHandle(handle: StreamHandle): void {\n this.streamManager.setHandle(handle);\n this.feedbackCoordinator.setStreamHandle(handle);\n }\n\n clearStreamHandle(): void {\n this.streamManager.clearHandle();\n this.feedbackCoordinator.endTask();\n }\n\n /** Last assistant visible plain text for a session (e.g. after a webchat stream). */\n getLastAssistantPlainText(sessionKey: string): string {\n return (\n this.sessionState.getLastAssistantText(sessionKey) ??\n this.agentManager.getLastAssistantContent(sessionKey) ??\n ''\n );\n }\n\n beginInboundTurn(sessionKey: string): void {\n this.sessionState.beginInboundTurn(sessionKey);\n }\n\n endInboundTurn(sessionKey: string): void {\n this.sessionState.endInboundTurn(sessionKey);\n }\n\n getInboundTurnDepth(sessionKey: string): number {\n return this.sessionState.getInboundTurnDepth(sessionKey);\n }\n\n async start(): Promise<void> {\n await this.sessionConfigStore.initialize();\n await this.hookHandler.trigger('gateway_start', { port: 0, host: 'cli' });\n await this.reconcileDreamingCronJob().catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.warn({ err, errorMessage: em }, `Dreaming cron reconcile failed: ${em}`);\n });\n log.debug('Agent service started');\n void this.inboundLoop.start().catch((err) => {\n const em = err instanceof Error ? err.message : String(err);\n log.error({ err, errorMessage: em }, `Inbound loop failed: ${em}`);\n });\n }\n\n stop(): Promise<void> {\n this.inboundLoop.stop();\n this.agentManager.dispose();\n this.dispose();\n\n this.hookHandler.trigger('gateway_stop', { reason: 'stopped' });\n log.debug('Agent service stopped');\n return Promise.resolve();\n }\n\n /**\n * Reconcile managed Dreaming cron job against the current effective config.\n * Safe to call after config saves to apply changes without restarting the process.\n */\n async reconcileDreamingNow(): Promise<void> {\n await this.reconcileDreamingCronJob();\n }\n\n private async reconcileDreamingCronJob(): Promise<void> {\n const cron = this.config.getCronService?.();\n if (!cron) {\n return;\n }\n await reconcileManagedDreamingCronJobs(cron, this.effectiveAppConfig());\n }\n\n /**\n * Persist agent messages with the same sanitizer + transcript hygiene as AgentOrchestrator.\n * Uses persistence hygiene so `thinking` blocks remain on disk for the web UI (LLM load path still drops them).\n */\n private notifySessionTitleUpdated(sessionKey: string, name: string): void {\n this.onSessionMetadataUpdated?.(sessionKey, { name });\n }\n\n /** Fire-and-forget provisional title from first user text (webchat sidebar). */\n enqueueProvisionalSessionTitle(sessionKey: string, userText: string): void {\n void (async () => {\n try {\n await maybeSetProvisionalSessionTitle(\n this.sessionStore,\n sessionKey,\n userText,\n (sk, name) => this.notifySessionTitleUpdated(sk, name),\n );\n } catch (err) {\n log.warn({ err, sessionKey }, 'Provisional session title failed');\n }\n })();\n }\n\n /**\n * Fire-and-forget LLM refine after turn persist; skips user-locked and finalized LLM titles.\n */\n private enqueueMaybeAutoTitleAfterPersist(sessionKey: string): void {\n void (async () => {\n try {\n let modelRef =\n getAgentDefaultModelRef(this.config.config ?? ({} as Config)) ?? this.config.model;\n if (!modelRef?.trim()) {\n try {\n modelRef = this.modelManager.getModelForSession(sessionKey);\n } catch {\n modelRef = undefined;\n }\n }\n await maybeRefineSessionTitleWithLlm(\n this.sessionStore,\n sessionKey,\n modelRef?.trim() || undefined,\n (sk, name) => this.notifySessionTitleUpdated(sk, name),\n );\n } catch (err) {\n log.warn({ err, sessionKey }, 'Auto session title failed');\n }\n })();\n }\n\n private prepareLoadedSessionMessages(sessionKey: string, messages: AgentMessage[]): AgentMessage[] {\n let out = cleanTrailingErrors(messages);\n try {\n const model = this.modelManager.getResolvedModelForSession(sessionKey);\n out = tryApplySessionTranscriptHygiene(out, model);\n } catch (err) {\n log.warn({ err, sessionKey }, 'Transcript hygiene on load skipped');\n }\n return out;\n }\n\n private async resolveSessionEndpoint(sessionKey: string): Promise<{ channel: string; chatId: string }> {\n const metadata = await this.sessionStore.getMetadata(sessionKey).catch(() => null);\n const channel = metadata?.routing?.source?.trim() || metadata?.sourceChannel?.trim() || 'cli';\n const chatId = metadata?.routing?.peerId?.trim() || metadata?.sourceChatId?.trim() || 'main';\n return { channel, chatId };\n }\n\n private initSessionContext(\n sessionKey: string,\n channel: string,\n chatId: string,\n senderId = '',\n ): SessionContext {\n const context: SessionContext = {\n sessionKey,\n channel,\n chatId,\n senderId,\n isGroup: false,\n };\n\n this.contextMiddleware.onRequest({\n sessionKey,\n userId: context.senderId,\n channel,\n chatId,\n });\n\n // Direct turn entry points (one-shot + streaming generator) cannot wrap their\n // body in `sessionContextManager.runWith(ctx, fn)` cleanly — the streaming\n // path is an async generator, and both flows already use a try/finally for\n // side-effect cleanup. We use `enter` so the context is visible via ALS for\n // every async resource launched after this call returns. The context is\n // overwritten or cleared by the next direct turn (each direct turn calls\n // `initSessionContext` first), so there is no cross-session leak in practice.\n this.sessionContextManager.enter(context);\n this.feedbackCoordinator.setContext(context);\n this.agentManager.getOrCreateAgent(sessionKey);\n this.setupSessionEventHandling(sessionKey);\n\n return context;\n }\n\n /**\n * Persist inbound file attachments to the global media store.\n */\n async prepareInboundAttachments(\n _sessionKey: string,\n attachments?: InboundAttachmentInput[],\n ): Promise<MediaRef[] | undefined> {\n return persistInboundAttachments(attachments, {\n resolveUri: async (uri) => {\n const target = parseNoteAttachmentTarget(uri);\n if (!target) return null;\n const notesService = (this as unknown as {\n notesServiceInstance?: {\n getAttachmentPath(noteId: string, attachmentId: string): Promise<{\n filePath: string;\n mimeType?: string;\n fileName?: string;\n } | null>;\n };\n }).notesServiceInstance;\n if (!notesService) return null;\n const located = await notesService.getAttachmentPath(target.noteId, target.attachmentId);\n if (!located) return null;\n const buffer = await readFile(located.filePath);\n return {\n buffer,\n path: located.filePath,\n mimeType: located.mimeType,\n name: located.fileName,\n size: buffer.byteLength,\n };\n },\n });\n }\n\n private endDirectRequestContext(): void {\n // `sessionContextManager` is ALS-backed: the context for the current async\n // chain drops automatically when the chain unwinds. The feedback\n // coordinator + context middleware still use singleton state, so clear\n // them explicitly here.\n this.feedbackCoordinator.clearContext();\n this.contextMiddleware.onResponse();\n }\n\n /**\n * Reset a session's transcript and drop the in-memory agent so the next turn\n * reloads from disk. Combines two collaborators (sessionStore + agentManager)\n * so it stays on `AgentService`; pure sessionStore reads should use\n * `agentService.sessionStore.*` directly.\n */\n async clearSessionMessages(key: string): Promise<void> {\n await this.sessionStore.saveMessages(key, []);\n this.agentManager.removeAgent(key);\n }\n\n /**\n * Reset session transcript (archive + new session id) and evict in-memory agent state.\n * Preserves the session key and persisted per-session overrides.\n */\n async resetSession(\n key: string,\n ): Promise<{ sessionId: string; previousSessionId: string } | null> {\n const { abortEmbeddedRun } = await import('./embedded/runs.js');\n const { retireSessionMcpRuntimeForSessionKey } = await import('./mcp/bundle-mcp-tools.js');\n await abortEmbeddedRun(key);\n const outcome = await this.sessionStore.reset(key);\n if (!outcome) {\n return null;\n }\n this.agentManager.removeAgent(key);\n await retireSessionMcpRuntimeForSessionKey({ sessionKey: key, reason: 'session-reset' });\n return outcome;\n }\n\n /**\n * Drop in-memory agent so the next turn reloads transcript from disk (e.g. after checkpoint restore).\n */\n evictSessionAgent(sessionKey: string): void {\n this.agentManager.removeAgent(sessionKey);\n }\n\n /**\n * Load session working directory override into AgentManager, ensure directory exists.\n * Call before AgentManager.getOrCreateAgent for this session.\n */\n /** Workspace root for UI file tree / editor (same as agent tools after hydration). */\n async getEffectiveWorkspacePathForSession(sessionKey: string): Promise<string> {\n await this.sessionHydrator.workspace(sessionKey);\n const cfg = this.config.config!;\n const sc = await this.sessionConfigStore.get(sessionKey);\n return effectiveWorkspacePathForSession(cfg, sessionKey, sc);\n }\n\n /**\n * Best-effort timezone resolution for webchat envelope timestamps.\n * Reads the global user profile and extracts a `Timezone:` line.\n */\n resolveUserTimezoneForSession(sessionKey: string): string | undefined {\n void sessionKey;\n try {\n const cfg = this.effectiveAppConfig();\n if (!cfg) return undefined;\n const userPath = resolveUserProfilePath();\n if (!existsSync(userPath)) return undefined;\n const raw = readFileSync(userPath, 'utf-8');\n const match = raw.match(/Timezone:\\s*(.+)/i);\n const tz = match?.[1]?.trim();\n return tz || undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Setup event handling for a specific session\n */\n private setupSessionEventHandling(sessionKey: string): void {\n if (this.sessionState.hasSessionEventUnsubscriber(sessionKey)) {\n return;\n }\n\n const unsubscribe = this.agentManager.subscribeToSession(sessionKey, (event) => {\n this.handleSessionEvent(sessionKey, event);\n });\n\n if (unsubscribe) {\n this.sessionState.setSessionEventUnsubscriber(sessionKey, unsubscribe);\n }\n }\n\n /**\n * Handle events from a specific session's agent\n */\n private handleSessionEvent(sessionKey: string, event: AgentEvent): void {\n const currentContext = this.sessionContextManager.getContext();\n if (!currentContext) {\n // Inbound `finally` clears context before trailing agent `message_update` events finish — ignore (not a bug).\n return;\n }\n\n if (currentContext.sessionKey !== sessionKey) {\n // Event from a different session — still process with current context where applicable\n this.agentEventHandler.handle(event, currentContext);\n return;\n }\n\n // Handle streaming updates for the current session\n if (event.type === 'message_update') {\n const msgEvent = event as Extract<AgentEvent, { type: 'message_update' }>;\n if (msgEvent.message?.role === 'assistant') {\n const content = msgEvent.message.content;\n const text = Array.isArray(content)\n ? extractTextContent(content as Array<{ type: string; text?: string }>)\n : String(content);\n\n this.streamManager.update(text);\n }\n }\n\n this.agentEventHandler.handle(event, currentContext);\n }\n\n private async checkAndCompact(sessionKey: string, messages: AgentMessage[]): Promise<void> {\n const contextWindow = this.getContextWindow();\n const prep = this.sessionStore.prepareCompaction(sessionKey, messages, contextWindow);\n if (!prep.needsCompaction) return;\n\n log.info({ sessionKey, reason: prep.stats?.reason, usagePercent: prep.stats?.usagePercent }, 'Session needs compaction');\n\n const result = await this.sessionStore.compact(sessionKey, messages, contextWindow, undefined, false);\n await this.hookHandler.trigger('after_compaction', {\n messageCount: messages.length,\n tokenCount: result.tokensBefore,\n compactedCount: messages.length - result.firstKeptIndex,\n });\n log.info({ sessionKey, tokensBefore: result.tokensBefore, tokensAfter: result.tokensAfter }, 'Session compacted');\n\n await this.checkAndFlush(sessionKey, messages, result, contextWindow);\n }\n\n private resolveMemoryFlushConfig(): MemoryFlushConfig {\n return {\n enabled: true,\n threshold: 0.88,\n softThresholdTokens: 4000,\n afterCompactions: 2,\n maxEntryChars: 2000,\n includeToolHistory: true,\n };\n }\n\n private async checkAndFlush(\n sessionKey: string,\n messages: AgentMessage[],\n compactionResult: import('./memory/compaction.js').CompactionResult,\n contextWindow: number,\n ): Promise<void> {\n const cfg = this.resolveMemoryFlushConfig();\n if (!cfg.enabled) return;\n\n const metadata = await this.sessionStore.getMetadata(sessionKey);\n const compactedCount = metadata?.compactedCount ?? 0;\n\n const totalTokens = await this.sessionStore.estimateTokenUsage(sessionKey, messages);\n const usagePercent = totalTokens / contextWindow;\n const effectiveThreshold = Math.max(0.5, cfg.threshold - cfg.softThresholdTokens / contextWindow);\n const exceedsThreshold = usagePercent >= effectiveThreshold;\n\n const tooManyCompactions = compactedCount >= cfg.afterCompactions;\n\n if (!exceedsThreshold && !tooManyCompactions) return;\n\n const workspaceDir = this.agentManager.getResolvedWorkspaceForSession(sessionKey);\n const flushResult = await this.memoryFlushService.flush({\n workspaceDir,\n sessionKey,\n compactionResult,\n config: cfg,\n });\n\n if (flushResult.flushed) {\n log.info({ sessionKey, entryPath: flushResult.entryPath, entryLength: flushResult.entryLength }, 'Memory flushed to daily notes');\n const currentMeta = await this.sessionStore.getMetadata(sessionKey);\n await this.sessionStore.updateMetadata(sessionKey, {\n lastFlushedAt: new Date().toISOString(),\n flushCount: (currentMeta?.flushCount ?? 0) + 1,\n });\n }\n }\n\n private getContextWindow(): number {\n return 128000;\n }\n\n private dispose(): void {\n this.sessionTracker.dispose();\n this.sessionState.disposeAll();\n this.agentManager.dispose();\n this.workflowProgressBrokerHandle?.dispose();\n this.workflowProgressBrokerHandle = null;\n }\n}\n"],"mappings":"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"}
@@ -3,19 +3,42 @@ import { Type } from "@sinclair/typebox";
3
3
  const CuratedMemorySchema = Type.Object({
4
4
  action: Type.Union([
5
5
  Type.Literal("add"),
6
+ Type.Literal("propose"),
6
7
  Type.Literal("replace"),
7
8
  Type.Literal("remove"),
8
9
  Type.Literal("read")
9
10
  ]),
10
11
  target: Type.Union([Type.Literal("memory"), Type.Literal("user")]),
12
+ kind: Type.Optional(Type.Union([
13
+ Type.Literal("user_profile"),
14
+ Type.Literal("agent_note"),
15
+ Type.Literal("workspace_fact"),
16
+ Type.Literal("daily_note"),
17
+ Type.Literal("session_summary"),
18
+ Type.Literal("derived_insight"),
19
+ Type.Literal("task_lesson"),
20
+ Type.Literal("tool_preference"),
21
+ Type.Literal("long_term_goal")
22
+ ])),
23
+ sensitivity: Type.Optional(Type.Union([
24
+ Type.Literal("normal"),
25
+ Type.Literal("personal"),
26
+ Type.Literal("secret"),
27
+ Type.Literal("regulated")
28
+ ])),
29
+ confidence: Type.Optional(Type.Number({
30
+ minimum: 0,
31
+ maximum: 1
32
+ })),
33
+ source_text: Type.Optional(Type.String({ description: "Short evidence excerpt supporting a proposed memory." })),
11
34
  content: Type.Optional(Type.String({ description: "Required for add; new text for replace." })),
12
35
  old_text: Type.Optional(Type.String({ description: "Substring to match one entry for replace/remove." }))
13
36
  });
14
37
  function createCuratedMemoryTool(getMemoryManager) {
15
38
  return {
16
39
  name: "curated_memory",
17
- label: "🧠 Curated memory",
18
- description: "Read or edit bounded curated memory under agent home `memories/` (MEMORY.md = agent notes, USER.md = user profile). Entries are separated by a section-sign delimiter (see store format). System prompt shows a frozen snapshot from session start; this tool reads/writes live state on disk. Use add/replace/remove for structured updates; use read to inspect current entries.",
40
+ label: "Curated memory",
41
+ description: "Read, edit, or propose bounded curated memory (MEMORY.md = agent notes, user/MEMORY.md = global user memory). Use propose when a memory should be reviewed by the user before it becomes active recall context. Entries are separated by a section-sign delimiter (see store format). System prompt shows a frozen snapshot from session start; this tool reads/writes live state on disk. Use add/replace/remove for structured updates; use read to inspect current entries.",
19
42
  parameters: CuratedMemorySchema,
20
43
  async execute(_toolCallId, params, _signal) {
21
44
  const memoryManager = getMemoryManager();
@@ -58,6 +81,38 @@ function createCuratedMemoryTool(getMemoryManager) {
58
81
  details: { success: true }
59
82
  };
60
83
  }
84
+ if (action === "propose") {
85
+ const p = params;
86
+ const content = p.content?.trim() ?? "";
87
+ const sourceText = p.source_text?.trim();
88
+ const result = await memoryManager.write({
89
+ kind: p.kind ?? (target === "user" ? "user_profile" : "agent_note"),
90
+ target,
91
+ content,
92
+ status: "candidate",
93
+ sensitivity: p.sensitivity ?? (target === "user" ? "personal" : "normal"),
94
+ confidence: p.confidence,
95
+ evidence: sourceText ? [{ sourceText }] : void 0
96
+ });
97
+ if (!result.success) return {
98
+ content: [{
99
+ type: "text",
100
+ text: result.error ?? "Unknown error"
101
+ }],
102
+ details: { error: result.error }
103
+ };
104
+ return {
105
+ content: [{
106
+ type: "text",
107
+ text: result.message ?? "Memory candidate added to inbox"
108
+ }],
109
+ details: {
110
+ success: true,
111
+ recordId: result.record?.id,
112
+ status: result.record?.status
113
+ }
114
+ };
115
+ }
61
116
  if (action === "replace") {
62
117
  const oldText = params.old_text?.trim() ?? "";
63
118
  const newContent = params.content?.trim() ?? "";
@@ -1 +1 @@
1
- {"version":3,"file":"curated-memory-tool.js","names":[],"sources":["../../../../src/agent/tools/curated-memory-tool.ts"],"sourcesContent":["// Curated memory tool — agent home `memories/MEMORY.md` + `USER.md` (session snapshot + live edits)\nimport { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\n\nimport type { MemoryManager } from '../memory/manager.js';\n\nconst CuratedMemorySchema = Type.Object({\n action: Type.Union([\n Type.Literal('add'),\n Type.Literal('replace'),\n Type.Literal('remove'),\n Type.Literal('read'),\n ]),\n target: Type.Union([Type.Literal('memory'), Type.Literal('user')]),\n content: Type.Optional(Type.String({ description: 'Required for add; new text for replace.' })),\n old_text: Type.Optional(\n Type.String({ description: 'Substring to match one entry for replace/remove.' }),\n ),\n});\n\ntype CuratedMemoryParams = {\n action: 'add' | 'replace' | 'remove' | 'read';\n target: 'memory' | 'user';\n content?: string;\n old_text?: string;\n};\n\nexport function createCuratedMemoryTool(\n getMemoryManager: () => MemoryManager,\n): AgentTool {\n return {\n name: 'curated_memory',\n label: '🧠 Curated memory',\n description:\n 'Read or edit bounded curated memory under agent home `memories/` (MEMORY.md = agent notes, USER.md = user profile). Entries are separated by a section-sign delimiter (see store format). System prompt shows a frozen snapshot from session start; this tool reads/writes live state on disk. Use add/replace/remove for structured updates; use read to inspect current entries.',\n parameters: CuratedMemorySchema,\n\n async execute(\n _toolCallId: string,\n params: any,\n _signal?: AbortSignal,\n ): Promise<AgentToolResult<{}>> {\n const memoryManager = getMemoryManager();\n const { action, target } = params as CuratedMemoryParams;\n\n try {\n if (action === 'read') {\n const records = await memoryManager.list({ target });\n const entries = records.map((record) => record.content);\n const text = JSON.stringify(\n { target, entries, count: entries.length },\n null,\n 2,\n );\n return {\n content: [{ type: 'text', text }],\n details: { entries },\n };\n }\n\n if (action === 'add') {\n const content = (params as CuratedMemoryParams).content?.trim() ?? '';\n const result = await memoryManager.write({\n kind: target === 'user' ? 'user_profile' : 'agent_note',\n target,\n content,\n });\n if (!result.success) {\n return {\n content: [{ type: 'text', text: result.error ?? 'Unknown error' }],\n details: { error: result.error },\n };\n }\n memoryManager.onMemoryWrite('add', target, content);\n return {\n content: [{ type: 'text', text: result.message ?? 'OK' }],\n details: { success: true },\n };\n }\n\n if (action === 'replace') {\n const oldText = (params as CuratedMemoryParams).old_text?.trim() ?? '';\n const newContent = (params as CuratedMemoryParams).content?.trim() ?? '';\n const result = await memoryManager.update({ target, matchText: oldText, content: newContent });\n if (!result.success) {\n return {\n content: [{ type: 'text', text: result.error ?? 'Unknown error' }],\n details: { error: result.error },\n };\n }\n memoryManager.onMemoryWrite('replace', target, newContent);\n return {\n content: [{ type: 'text', text: result.message ?? 'OK' }],\n details: { success: true },\n };\n }\n\n const oldText = (params as CuratedMemoryParams).old_text?.trim() ?? '';\n const result = await memoryManager.delete({ target, matchText: oldText });\n if (!result.success) {\n return {\n content: [{ type: 'text', text: result.error ?? 'Unknown error' }],\n details: { error: result.error },\n };\n }\n memoryManager.onMemoryWrite('remove', target, oldText);\n return {\n content: [{ type: 'text', text: result.message ?? 'OK' }],\n details: { success: true },\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n content: [{ type: 'text', text: `curated_memory error: ${message}` }],\n details: { error: message },\n };\n }\n },\n } as any;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"curated-memory-tool.js","names":[],"sources":["../../../../src/agent/tools/curated-memory-tool.ts"],"sourcesContent":["// Curated memory tool — agent home `memories/MEMORY.md` + global `user/MEMORY.md` (session snapshot + live edits)\nimport { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\n\nimport type { MemoryManager } from '../memory/manager.js';\n\nconst CuratedMemorySchema = Type.Object({\n action: Type.Union([\n Type.Literal('add'),\n Type.Literal('propose'),\n Type.Literal('replace'),\n Type.Literal('remove'),\n Type.Literal('read'),\n ]),\n target: Type.Union([Type.Literal('memory'), Type.Literal('user')]),\n kind: Type.Optional(Type.Union([\n Type.Literal('user_profile'),\n Type.Literal('agent_note'),\n Type.Literal('workspace_fact'),\n Type.Literal('daily_note'),\n Type.Literal('session_summary'),\n Type.Literal('derived_insight'),\n Type.Literal('task_lesson'),\n Type.Literal('tool_preference'),\n Type.Literal('long_term_goal'),\n ])),\n sensitivity: Type.Optional(Type.Union([\n Type.Literal('normal'),\n Type.Literal('personal'),\n Type.Literal('secret'),\n Type.Literal('regulated'),\n ])),\n confidence: Type.Optional(Type.Number({ minimum: 0, maximum: 1 })),\n source_text: Type.Optional(Type.String({ description: 'Short evidence excerpt supporting a proposed memory.' })),\n content: Type.Optional(Type.String({ description: 'Required for add; new text for replace.' })),\n old_text: Type.Optional(\n Type.String({ description: 'Substring to match one entry for replace/remove.' }),\n ),\n});\n\ntype CuratedMemoryParams = {\n action: 'add' | 'propose' | 'replace' | 'remove' | 'read';\n target: 'memory' | 'user';\n kind?: 'user_profile' | 'agent_note' | 'workspace_fact' | 'daily_note' | 'session_summary' | 'derived_insight' | 'task_lesson' | 'tool_preference' | 'long_term_goal';\n sensitivity?: 'normal' | 'personal' | 'secret' | 'regulated';\n confidence?: number;\n source_text?: string;\n content?: string;\n old_text?: string;\n};\n\nexport function createCuratedMemoryTool(\n getMemoryManager: () => MemoryManager,\n): AgentTool {\n return {\n name: 'curated_memory',\n label: 'Curated memory',\n description:\n 'Read, edit, or propose bounded curated memory (MEMORY.md = agent notes, user/MEMORY.md = global user memory). Use propose when a memory should be reviewed by the user before it becomes active recall context. Entries are separated by a section-sign delimiter (see store format). System prompt shows a frozen snapshot from session start; this tool reads/writes live state on disk. Use add/replace/remove for structured updates; use read to inspect current entries.',\n parameters: CuratedMemorySchema,\n\n async execute(\n _toolCallId: string,\n params: any,\n _signal?: AbortSignal,\n ): Promise<AgentToolResult<{}>> {\n const memoryManager = getMemoryManager();\n const { action, target } = params as CuratedMemoryParams;\n\n try {\n if (action === 'read') {\n const records = await memoryManager.list({ target });\n const entries = records.map((record) => record.content);\n const text = JSON.stringify(\n { target, entries, count: entries.length },\n null,\n 2,\n );\n return {\n content: [{ type: 'text', text }],\n details: { entries },\n };\n }\n\n if (action === 'add') {\n const content = (params as CuratedMemoryParams).content?.trim() ?? '';\n const result = await memoryManager.write({\n kind: target === 'user' ? 'user_profile' : 'agent_note',\n target,\n content,\n });\n if (!result.success) {\n return {\n content: [{ type: 'text', text: result.error ?? 'Unknown error' }],\n details: { error: result.error },\n };\n }\n memoryManager.onMemoryWrite('add', target, content);\n return {\n content: [{ type: 'text', text: result.message ?? 'OK' }],\n details: { success: true },\n };\n }\n\n if (action === 'propose') {\n const p = params as CuratedMemoryParams;\n const content = p.content?.trim() ?? '';\n const sourceText = p.source_text?.trim();\n const result = await memoryManager.write({\n kind: p.kind ?? (target === 'user' ? 'user_profile' : 'agent_note'),\n target,\n content,\n status: 'candidate',\n sensitivity: p.sensitivity ?? (target === 'user' ? 'personal' : 'normal'),\n confidence: p.confidence,\n evidence: sourceText ? [{ sourceText }] : undefined,\n });\n if (!result.success) {\n return {\n content: [{ type: 'text', text: result.error ?? 'Unknown error' }],\n details: { error: result.error },\n };\n }\n return {\n content: [{ type: 'text', text: result.message ?? 'Memory candidate added to inbox' }],\n details: { success: true, recordId: result.record?.id, status: result.record?.status },\n };\n }\n\n if (action === 'replace') {\n const oldText = (params as CuratedMemoryParams).old_text?.trim() ?? '';\n const newContent = (params as CuratedMemoryParams).content?.trim() ?? '';\n const result = await memoryManager.update({ target, matchText: oldText, content: newContent });\n if (!result.success) {\n return {\n content: [{ type: 'text', text: result.error ?? 'Unknown error' }],\n details: { error: result.error },\n };\n }\n memoryManager.onMemoryWrite('replace', target, newContent);\n return {\n content: [{ type: 'text', text: result.message ?? 'OK' }],\n details: { success: true },\n };\n }\n\n const oldText = (params as CuratedMemoryParams).old_text?.trim() ?? '';\n const result = await memoryManager.delete({ target, matchText: oldText });\n if (!result.success) {\n return {\n content: [{ type: 'text', text: result.error ?? 'Unknown error' }],\n details: { error: result.error },\n };\n }\n memoryManager.onMemoryWrite('remove', target, oldText);\n return {\n content: [{ type: 'text', text: result.message ?? 'OK' }],\n details: { success: true },\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n content: [{ type: 'text', text: `curated_memory error: ${message}` }],\n details: { error: message },\n };\n }\n },\n } as any;\n}\n"],"mappings":"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"}
@@ -48,10 +48,9 @@ function resolveExtractionModel(config) {
48
48
  if (ref) try {
49
49
  return resolveModel(ref);
50
50
  } catch {}
51
- for (const candidate of ["openai/gpt-4o-mini", "google/gemini-2.0-flash"]) try {
52
- return resolveModel(candidate);
53
- } catch {}
54
- return resolveModel(getDefaultModelSync(config));
51
+ const defaultRef = getDefaultModelSync(config);
52
+ if (!defaultRef) throw new Error("No global default model configured for web extraction");
53
+ return resolveModel(defaultRef);
55
54
  }
56
55
  function buildExtractionSystemPrompt(maxLength) {
57
56
  return `You are a web content extractor. Given raw HTML/text from a web page, extract the relevant content as clean, well-structured markdown.
@@ -193,9 +192,8 @@ async function synthesizeChunkSummaries(summaries, url, instruction, maxLength,
193
192
  return fallback.length > outputCap ? fallback.slice(0, outputCap) + "\n\n[...truncated due to synthesis failure]" : fallback;
194
193
  }
195
194
  }
196
- function resolveConfiguredMaxLength(config) {
197
- const value = (config?.agents)?.defaults?.webExtract?.maxLength;
198
- return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : DEFAULT_WEB_EXTRACT_MAX_LENGTH;
195
+ function resolveConfiguredMaxLength() {
196
+ return DEFAULT_WEB_EXTRACT_MAX_LENGTH;
199
197
  }
200
198
  function createWebExtractTool(deps) {
201
199
  return {
@@ -206,7 +204,7 @@ function createWebExtractTool(deps) {
206
204
  async execute(_toolCallId, params, signal) {
207
205
  const { url, instruction } = params;
208
206
  const cfg = deps.getConfig();
209
- const maxLength = params.maxLength ?? resolveConfiguredMaxLength(cfg);
207
+ const maxLength = params.maxLength ?? resolveConfiguredMaxLength();
210
208
  try {
211
209
  const safety = checkUrlSafety(url);
212
210
  if (!safety.safe) return {
@@ -1 +1 @@
1
- {"version":3,"file":"web-extract.js","names":[],"sources":["../../../../src/agent/tools/web-extract.ts"],"sourcesContent":["import { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\nimport { complete, type UserMessage } from '@earendil-works/pi-ai';\n\nimport type { Config } from '../../config/schema.js';\nimport { getDefaultModelSync, resolveModel } from '../../providers/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { checkUrlSafety, checkWebsiteBlocklist, cleanBase64Images } from './url-safety.js';\n\nconst log = createLogger('web-extract');\n\nexport const DEFAULT_WEB_EXTRACT_MAX_LENGTH = 15_000;\nexport const MAX_RAW_HTML_CHARS_FOR_WEB_EXTRACT = 200_000;\nconst EXTRACTION_TIMEOUT_MS = 30_000;\n\n// Large-document chunked processing thresholds (inspired by hermes-agent)\nconst MAX_CONTENT_SIZE = 2_000_000; // 2M chars — refuse entirely above this\nconst CHUNK_THRESHOLD = 500_000; // 500k chars — use chunked processing above this\nconst CHUNK_SIZE = 100_000; // 100k chars per chunk\nconst MAX_OUTPUT_SIZE = 15_000; // Hard cap on final output size\n\nconst WebExtractSchema = Type.Object({\n url: Type.String({ description: 'URL to extract content from' }),\n instruction: Type.Optional(\n Type.String({\n description:\n 'What to extract or focus on. Examples: \"main article text\", ' +\n '\"pricing table\", \"API documentation for the auth endpoint\". ' +\n 'If omitted, extracts the main content.',\n }),\n ),\n maxLength: Type.Optional(\n Type.Number({\n description: 'Maximum characters in the extracted content (default: from config or 15000)',\n }),\n ),\n});\n\nexport interface WebExtractDeps {\n getConfig: () => Config | undefined;\n}\n\nasync function fetchPageContent(url: string, signal?: AbortSignal): Promise<string> {\n const response = await fetch(url, {\n signal,\n headers: {\n 'User-Agent': 'Mozilla/5.0 (compatible; xopc/1.0)',\n Accept: 'text/html,application/xhtml+xml,text/plain,application/json',\n },\n redirect: 'follow',\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.includes('application/json')) {\n const json: unknown = await response.json();\n return JSON.stringify(json, null, 2);\n }\n\n return await response.text();\n}\n\nexport function stripHtmlBoilerplate(\n html: string,\n maxRaw = MAX_RAW_HTML_CHARS_FOR_WEB_EXTRACT,\n): string {\n let cleaned = html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<svg[\\s\\S]*?<\\/svg>/gi, '')\n .replace(/<noscript[\\s\\S]*?<\\/noscript>/gi, '')\n .replace(/<!--[\\s\\S]*?-->/g, '');\n\n // Remove base64 images early to avoid wasting characters on them\n cleaned = cleanBase64Images(cleaned);\n cleaned = cleaned.replace(/\\s+/g, ' ').trim();\n\n if (cleaned.length > maxRaw) {\n cleaned = cleaned.slice(0, maxRaw) + '\\n\\n[...truncated]';\n }\n\n return cleaned;\n}\n\nfunction resolveExtractionModel(config?: Config) {\n const envRef = process.env.XOPC_WEB_EXTRACT_MODEL?.trim();\n const ref = envRef;\n\n if (ref) {\n try {\n return resolveModel(ref);\n } catch {\n /* fall through */\n }\n }\n\n for (const candidate of ['openai/gpt-4o-mini', 'google/gemini-2.0-flash']) {\n try {\n return resolveModel(candidate);\n } catch {\n /* next */\n }\n }\n\n return resolveModel(getDefaultModelSync(config));\n}\n\nfunction buildExtractionSystemPrompt(maxLength: number): string {\n return (\n 'You are a web content extractor. Given raw HTML/text from a web page, ' +\n 'extract the relevant content as clean, well-structured markdown.\\n\\n' +\n 'Rules:\\n' +\n '- Remove navigation, headers, footers, ads, cookie banners, sidebars\\n' +\n '- Preserve the main content structure (headings, lists, tables, code blocks)\\n' +\n '- Keep URLs for important links\\n' +\n '- If the page has structured data (tables, specs), preserve the structure\\n' +\n `- Keep output under ${maxLength} characters\\n` +\n '- If content is truncated, note what was cut\\n' +\n '- Output ONLY the extracted content, no meta-commentary'\n );\n}\n\n// ---------------------------------------------------------------------------\n// LLM call helper — shared by single-pass and chunked extraction\n// ---------------------------------------------------------------------------\n\nfunction buildTimeoutSignal(signal: AbortSignal | undefined): AbortSignal | undefined {\n const timeoutSignal =\n typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function'\n ? AbortSignal.timeout(EXTRACTION_TIMEOUT_MS)\n : undefined;\n if (signal && timeoutSignal && typeof AbortSignal.any === 'function') {\n return AbortSignal.any([signal, timeoutSignal]);\n }\n return signal ?? timeoutSignal;\n}\n\nfunction extractTextFromCompletion(result: { content?: unknown }): string {\n let text = '';\n if (Array.isArray(result.content)) {\n for (const c of result.content) {\n if (c && typeof c === 'object' && (c as { type?: string }).type === 'text') {\n text += String((c as { text?: string }).text || '');\n }\n }\n }\n return text.trim();\n}\n\nasync function callExtractionLlm(\n systemPrompt: string,\n userPrompt: string,\n maxTokens: number,\n config: Config | undefined,\n signal: AbortSignal | undefined,\n): Promise<string> {\n const model = resolveExtractionModel(config);\n const userMsg: UserMessage = {\n role: 'user',\n content: `${systemPrompt}\\n\\n---\\n\\n${userPrompt}`,\n timestamp: Date.now(),\n };\n\n const result = await complete(\n model,\n { messages: [userMsg] },\n {\n maxTokens,\n temperature: 0.2,\n signal: buildTimeoutSignal(signal) as AbortSignal,\n },\n );\n\n return extractTextFromCompletion(result);\n}\n\n// ---------------------------------------------------------------------------\n// Single-pass extraction (content < CHUNK_THRESHOLD)\n// ---------------------------------------------------------------------------\n\nasync function extractWithLlm(\n rawContent: string,\n url: string,\n instruction: string | undefined,\n maxLength: number,\n config: Config | undefined,\n signal: AbortSignal | undefined,\n): Promise<string> {\n const contentLength = rawContent.length;\n\n // Refuse absurdly large content\n if (contentLength > MAX_CONTENT_SIZE) {\n const sizeMb = (contentLength / 1_000_000).toFixed(1);\n return (\n `[Content too large to process: ${sizeMb}MB. ` +\n 'Try using web_fetch for raw text, or search for a more focused source.]'\n );\n }\n\n // Route to chunked processing for large documents\n if (contentLength > CHUNK_THRESHOLD) {\n log.info(\n { url, contentLength, chunkSize: CHUNK_SIZE },\n 'Large document detected, using chunked extraction',\n );\n return extractLargeDocumentChunked(rawContent, url, instruction, maxLength, config, signal);\n }\n\n // Standard single-pass extraction\n const systemPrompt = buildExtractionSystemPrompt(maxLength);\n const body = instruction\n ? `Extract from this page (${url}):\\n\\nFOCUS: ${instruction}\\n\\n---\\n\\n${rawContent}`\n : `Extract the main content from this page (${url}):\\n\\n---\\n\\n${rawContent}`;\n\n const text = await callExtractionLlm(\n systemPrompt,\n body,\n Math.min(Math.ceil(maxLength / 3), 8000),\n config,\n signal,\n );\n\n if (!text) return 'No content could be extracted.';\n\n const outputCap = Math.min(maxLength, MAX_OUTPUT_SIZE);\n if (text.length > outputCap) {\n return text.slice(0, outputCap) + '\\n\\n[...output truncated to maxLength]';\n }\n\n log.debug(\n { url, originalChars: contentLength, extractedChars: text.length },\n 'Single-pass extraction complete',\n );\n return text;\n}\n\n// ---------------------------------------------------------------------------\n// Chunked extraction for large documents (inspired by hermes-agent)\n//\n// Flow: split → parallel chunk summaries → synthesis pass\n// ---------------------------------------------------------------------------\n\nasync function extractLargeDocumentChunked(\n content: string,\n url: string,\n instruction: string | undefined,\n maxLength: number,\n config: Config | undefined,\n signal: AbortSignal | undefined,\n): Promise<string> {\n // Split content into chunks\n const chunks: string[] = [];\n for (let offset = 0; offset < content.length; offset += CHUNK_SIZE) {\n chunks.push(content.slice(offset, offset + CHUNK_SIZE));\n }\n log.info({ url, chunkCount: chunks.length, chunkSize: CHUNK_SIZE }, 'Split into chunks');\n\n // Summarize each chunk in parallel\n const chunkPromises = chunks.map(async (chunk, index) => {\n const chunkLabel = `[Chunk ${index + 1}/${chunks.length}]`;\n try {\n const systemPrompt =\n 'You are an expert content analyst processing a SECTION of a larger document. ' +\n 'Extract and summarize the key information from THIS SECTION ONLY.\\n\\n' +\n 'Guidelines:\\n' +\n '- Do NOT write introductions or conclusions — this is a partial document\\n' +\n '- Extract ALL key facts, figures, data points, and insights\\n' +\n '- Preserve important quotes, code snippets, and specific details verbatim\\n' +\n '- Use bullet points and structured formatting\\n' +\n '- Note references to other sections without trying to resolve them';\n\n const focusLine = instruction ? `\\nFOCUS: ${instruction}\\n` : '';\n const userPrompt =\n `${chunkLabel} Extract key information from this section of ${url}:` +\n `${focusLine}\\n\\n${chunk}`;\n\n const summary = await callExtractionLlm(systemPrompt, userPrompt, 6000, config, signal);\n if (summary) {\n log.debug(\n { url, chunk: index + 1, originalChars: chunk.length, summaryChars: summary.length },\n 'Chunk summarized',\n );\n }\n return { index, summary: summary || null };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n log.warn({ url, chunk: index + 1, error: message }, 'Chunk summarization failed');\n return { index, summary: null };\n }\n });\n\n const chunkResults = await Promise.all(chunkPromises);\n\n // Collect successful summaries in order\n const summaries: string[] = [];\n for (const { index, summary } of chunkResults.sort((a, b) => a.index - b.index)) {\n if (summary) {\n summaries.push(`## Section ${index + 1}\\n${summary}`);\n }\n }\n\n if (summaries.length === 0) {\n // All chunks failed — fall back to truncated raw content\n const outputCap = Math.min(maxLength, MAX_OUTPUT_SIZE);\n const truncated = content.slice(0, outputCap);\n return truncated + '\\n\\n[...all chunk summaries failed; showing truncated raw content]';\n }\n\n log.info(\n { url, successfulChunks: summaries.length, totalChunks: chunks.length },\n 'Chunk summaries collected, synthesizing',\n );\n\n // Single chunk — return directly\n if (summaries.length === 1) {\n const result = summaries[0];\n const outputCap = Math.min(maxLength, MAX_OUTPUT_SIZE);\n return result.length > outputCap\n ? result.slice(0, outputCap) + '\\n\\n[...truncated]'\n : result;\n }\n\n // Synthesize multiple chunk summaries into one cohesive summary\n return synthesizeChunkSummaries(summaries, url, instruction, maxLength, config, signal, content.length);\n}\n\nasync function synthesizeChunkSummaries(\n summaries: string[],\n url: string,\n instruction: string | undefined,\n maxLength: number,\n config: Config | undefined,\n signal: AbortSignal | undefined,\n originalContentLength: number,\n): Promise<string> {\n const outputCap = Math.min(maxLength, MAX_OUTPUT_SIZE);\n\n const systemPrompt =\n 'You synthesize multiple section summaries into one cohesive, comprehensive summary. ' +\n 'Remove redundancy, preserve all key facts and actionable information, ' +\n 'and organize with clear markdown structure.';\n\n const focusLine = instruction ? `\\nFOCUS: ${instruction}\\n` : '';\n const combined = summaries.join('\\n\\n---\\n\\n');\n const userPrompt =\n `Synthesize these section summaries from ${url} into ONE unified markdown summary ` +\n `(under ${outputCap} characters):${focusLine}\\n\\n${combined}`;\n\n try {\n const synthesized = await callExtractionLlm(systemPrompt, userPrompt, 8000, config, signal);\n\n if (!synthesized) {\n // Synthesis LLM returned empty — fall back to concatenation\n log.warn({ url }, 'Synthesis returned empty, concatenating chunk summaries');\n const fallback = summaries.join('\\n\\n');\n return fallback.length > outputCap\n ? fallback.slice(0, outputCap) + '\\n\\n[...truncated]'\n : fallback;\n }\n\n const finalOutput = synthesized.length > outputCap\n ? synthesized.slice(0, outputCap) + '\\n\\n[...summary truncated for context management]'\n : synthesized;\n\n const compressionRatio = finalOutput.length / originalContentLength;\n log.info(\n {\n url,\n originalChars: originalContentLength,\n extractedChars: finalOutput.length,\n compressionPercent: `${(compressionRatio * 100).toFixed(1)}%`,\n },\n 'Chunked extraction complete',\n );\n\n return finalOutput;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n log.warn({ url, error: message }, 'Synthesis failed, concatenating chunk summaries');\n const fallback = summaries.join('\\n\\n');\n return fallback.length > outputCap\n ? fallback.slice(0, outputCap) + '\\n\\n[...truncated due to synthesis failure]'\n : fallback;\n }\n}\n\ntype WebExtractParams = { url: string; instruction?: string; maxLength?: number };\n\nfunction resolveConfiguredMaxLength(config: Config | undefined): number {\n const value = (config?.agents as unknown as {\n defaults?: { webExtract?: { maxLength?: unknown } };\n } | undefined)?.defaults?.webExtract?.maxLength;\n return typeof value === 'number' && Number.isFinite(value) && value > 0\n ? Math.trunc(value)\n : DEFAULT_WEB_EXTRACT_MAX_LENGTH;\n}\n\nexport function createWebExtractTool(deps: WebExtractDeps): AgentTool {\n return {\n name: 'web_extract',\n label: '📄 Web Extract',\n description:\n 'Extract and summarize content from a web page using LLM-powered extraction.\\n\\n' +\n 'Unlike web_fetch (which returns cleaned plain text from HTML), this tool uses a ' +\n 'dedicated model pass to produce focused markdown. Use when you need semantic extraction ' +\n 'or a specific focus (instruction), not just a single readability pass.\\n\\n' +\n 'WHEN TO USE:\\n' +\n '- Reading articles, blog posts, documentation\\n' +\n '- Extracting specific data (pricing, specs, API docs)\\n' +\n '- Summarizing long pages to save context\\n\\n' +\n 'WHEN TO USE web_fetch INSTEAD:\\n' +\n '- Inspecting raw-ish page text quickly without an extra LLM call\\n' +\n '- Downloading JSON/API responses\\n' +\n '- Debugging page rendering issues',\n parameters: WebExtractSchema,\n\n async execute(\n _toolCallId: string,\n params: any,\n signal?: AbortSignal,\n ): Promise<AgentToolResult<{ url: string; extractedLength: number }>> {\n const { url, instruction } = params as WebExtractParams;\n const cfg = deps.getConfig();\n const maxLength = (params as WebExtractParams).maxLength ?? resolveConfiguredMaxLength(cfg);\n\n try {\n // SSRF protection\n const safety = checkUrlSafety(url);\n if (!safety.safe) {\n return {\n content: [{ type: 'text', text: `Blocked: ${safety.reason}` }],\n details: { url, extractedLength: 0 },\n };\n }\n\n // Website blocklist check\n const blocked = checkWebsiteBlocklist(url, cfg?.tools?.web?.blocklist);\n if (blocked) {\n return {\n content: [{ type: 'text', text: `Blocked: ${blocked.message}` }],\n details: { url, extractedLength: 0 },\n };\n }\n\n const rawContent = await fetchPageContent(url, signal);\n const cleaned = stripHtmlBoilerplate(rawContent);\n const trimmed = cleaned.trim();\n const looksStructured = trimmed.startsWith('{') || trimmed.startsWith('[');\n if (trimmed.length < 100 && !looksStructured) {\n return {\n content: [\n { type: 'text', text: `Page at ${url} appears to have no extractable content.` },\n ],\n details: { url, extractedLength: 0 },\n };\n }\n\n const extracted = await extractWithLlm(\n cleaned,\n url,\n instruction,\n maxLength,\n cfg,\n signal,\n );\n\n return {\n content: [{ type: 'text', text: extracted }],\n details: { url, extractedLength: extracted.length },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n log.warn({ url, error: message }, 'web_extract failed');\n return {\n content: [{ type: 'text', text: `Failed to extract content from ${url}: ${message}` }],\n details: { url, extractedLength: 0 },\n };\n }\n },\n } as any;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"web-extract.js","names":[],"sources":["../../../../src/agent/tools/web-extract.ts"],"sourcesContent":["import { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\nimport { complete, type UserMessage } from '@earendil-works/pi-ai';\n\nimport type { Config } from '../../config/schema.js';\nimport { getDefaultModelSync, resolveModel } from '../../providers/index.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { checkUrlSafety, checkWebsiteBlocklist, cleanBase64Images } from './url-safety.js';\n\nconst log = createLogger('web-extract');\n\nexport const DEFAULT_WEB_EXTRACT_MAX_LENGTH = 15_000;\nexport const MAX_RAW_HTML_CHARS_FOR_WEB_EXTRACT = 200_000;\nconst EXTRACTION_TIMEOUT_MS = 30_000;\n\n// Large-document chunked processing thresholds (inspired by hermes-agent)\nconst MAX_CONTENT_SIZE = 2_000_000; // 2M chars — refuse entirely above this\nconst CHUNK_THRESHOLD = 500_000; // 500k chars — use chunked processing above this\nconst CHUNK_SIZE = 100_000; // 100k chars per chunk\nconst MAX_OUTPUT_SIZE = 15_000; // Hard cap on final output size\n\nconst WebExtractSchema = Type.Object({\n url: Type.String({ description: 'URL to extract content from' }),\n instruction: Type.Optional(\n Type.String({\n description:\n 'What to extract or focus on. Examples: \"main article text\", ' +\n '\"pricing table\", \"API documentation for the auth endpoint\". ' +\n 'If omitted, extracts the main content.',\n }),\n ),\n maxLength: Type.Optional(\n Type.Number({\n description: 'Maximum characters in the extracted content (default: from config or 15000)',\n }),\n ),\n});\n\nexport interface WebExtractDeps {\n getConfig: () => Config | undefined;\n}\n\nasync function fetchPageContent(url: string, signal?: AbortSignal): Promise<string> {\n const response = await fetch(url, {\n signal,\n headers: {\n 'User-Agent': 'Mozilla/5.0 (compatible; xopc/1.0)',\n Accept: 'text/html,application/xhtml+xml,text/plain,application/json',\n },\n redirect: 'follow',\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const contentType = response.headers.get('content-type') ?? '';\n if (contentType.includes('application/json')) {\n const json: unknown = await response.json();\n return JSON.stringify(json, null, 2);\n }\n\n return await response.text();\n}\n\nexport function stripHtmlBoilerplate(\n html: string,\n maxRaw = MAX_RAW_HTML_CHARS_FOR_WEB_EXTRACT,\n): string {\n let cleaned = html\n .replace(/<script[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<svg[\\s\\S]*?<\\/svg>/gi, '')\n .replace(/<noscript[\\s\\S]*?<\\/noscript>/gi, '')\n .replace(/<!--[\\s\\S]*?-->/g, '');\n\n // Remove base64 images early to avoid wasting characters on them\n cleaned = cleanBase64Images(cleaned);\n cleaned = cleaned.replace(/\\s+/g, ' ').trim();\n\n if (cleaned.length > maxRaw) {\n cleaned = cleaned.slice(0, maxRaw) + '\\n\\n[...truncated]';\n }\n\n return cleaned;\n}\n\nfunction resolveExtractionModel(config?: Config) {\n const envRef = process.env.XOPC_WEB_EXTRACT_MODEL?.trim();\n const ref = envRef;\n\n if (ref) {\n try {\n return resolveModel(ref);\n } catch {\n /* fall through */\n }\n }\n\n const defaultRef = getDefaultModelSync(config);\n if (!defaultRef) {\n throw new Error('No global default model configured for web extraction');\n }\n\n return resolveModel(defaultRef);\n}\n\nfunction buildExtractionSystemPrompt(maxLength: number): string {\n return (\n 'You are a web content extractor. Given raw HTML/text from a web page, ' +\n 'extract the relevant content as clean, well-structured markdown.\\n\\n' +\n 'Rules:\\n' +\n '- Remove navigation, headers, footers, ads, cookie banners, sidebars\\n' +\n '- Preserve the main content structure (headings, lists, tables, code blocks)\\n' +\n '- Keep URLs for important links\\n' +\n '- If the page has structured data (tables, specs), preserve the structure\\n' +\n `- Keep output under ${maxLength} characters\\n` +\n '- If content is truncated, note what was cut\\n' +\n '- Output ONLY the extracted content, no meta-commentary'\n );\n}\n\n// ---------------------------------------------------------------------------\n// LLM call helper — shared by single-pass and chunked extraction\n// ---------------------------------------------------------------------------\n\nfunction buildTimeoutSignal(signal: AbortSignal | undefined): AbortSignal | undefined {\n const timeoutSignal =\n typeof AbortSignal !== 'undefined' && typeof AbortSignal.timeout === 'function'\n ? AbortSignal.timeout(EXTRACTION_TIMEOUT_MS)\n : undefined;\n if (signal && timeoutSignal && typeof AbortSignal.any === 'function') {\n return AbortSignal.any([signal, timeoutSignal]);\n }\n return signal ?? timeoutSignal;\n}\n\nfunction extractTextFromCompletion(result: { content?: unknown }): string {\n let text = '';\n if (Array.isArray(result.content)) {\n for (const c of result.content) {\n if (c && typeof c === 'object' && (c as { type?: string }).type === 'text') {\n text += String((c as { text?: string }).text || '');\n }\n }\n }\n return text.trim();\n}\n\nasync function callExtractionLlm(\n systemPrompt: string,\n userPrompt: string,\n maxTokens: number,\n config: Config | undefined,\n signal: AbortSignal | undefined,\n): Promise<string> {\n const model = resolveExtractionModel(config);\n const userMsg: UserMessage = {\n role: 'user',\n content: `${systemPrompt}\\n\\n---\\n\\n${userPrompt}`,\n timestamp: Date.now(),\n };\n\n const result = await complete(\n model,\n { messages: [userMsg] },\n {\n maxTokens,\n temperature: 0.2,\n signal: buildTimeoutSignal(signal) as AbortSignal,\n },\n );\n\n return extractTextFromCompletion(result);\n}\n\n// ---------------------------------------------------------------------------\n// Single-pass extraction (content < CHUNK_THRESHOLD)\n// ---------------------------------------------------------------------------\n\nasync function extractWithLlm(\n rawContent: string,\n url: string,\n instruction: string | undefined,\n maxLength: number,\n config: Config | undefined,\n signal: AbortSignal | undefined,\n): Promise<string> {\n const contentLength = rawContent.length;\n\n // Refuse absurdly large content\n if (contentLength > MAX_CONTENT_SIZE) {\n const sizeMb = (contentLength / 1_000_000).toFixed(1);\n return (\n `[Content too large to process: ${sizeMb}MB. ` +\n 'Try using web_fetch for raw text, or search for a more focused source.]'\n );\n }\n\n // Route to chunked processing for large documents\n if (contentLength > CHUNK_THRESHOLD) {\n log.info(\n { url, contentLength, chunkSize: CHUNK_SIZE },\n 'Large document detected, using chunked extraction',\n );\n return extractLargeDocumentChunked(rawContent, url, instruction, maxLength, config, signal);\n }\n\n // Standard single-pass extraction\n const systemPrompt = buildExtractionSystemPrompt(maxLength);\n const body = instruction\n ? `Extract from this page (${url}):\\n\\nFOCUS: ${instruction}\\n\\n---\\n\\n${rawContent}`\n : `Extract the main content from this page (${url}):\\n\\n---\\n\\n${rawContent}`;\n\n const text = await callExtractionLlm(\n systemPrompt,\n body,\n Math.min(Math.ceil(maxLength / 3), 8000),\n config,\n signal,\n );\n\n if (!text) return 'No content could be extracted.';\n\n const outputCap = Math.min(maxLength, MAX_OUTPUT_SIZE);\n if (text.length > outputCap) {\n return text.slice(0, outputCap) + '\\n\\n[...output truncated to maxLength]';\n }\n\n log.debug(\n { url, originalChars: contentLength, extractedChars: text.length },\n 'Single-pass extraction complete',\n );\n return text;\n}\n\n// ---------------------------------------------------------------------------\n// Chunked extraction for large documents (inspired by hermes-agent)\n//\n// Flow: split → parallel chunk summaries → synthesis pass\n// ---------------------------------------------------------------------------\n\nasync function extractLargeDocumentChunked(\n content: string,\n url: string,\n instruction: string | undefined,\n maxLength: number,\n config: Config | undefined,\n signal: AbortSignal | undefined,\n): Promise<string> {\n // Split content into chunks\n const chunks: string[] = [];\n for (let offset = 0; offset < content.length; offset += CHUNK_SIZE) {\n chunks.push(content.slice(offset, offset + CHUNK_SIZE));\n }\n log.info({ url, chunkCount: chunks.length, chunkSize: CHUNK_SIZE }, 'Split into chunks');\n\n // Summarize each chunk in parallel\n const chunkPromises = chunks.map(async (chunk, index) => {\n const chunkLabel = `[Chunk ${index + 1}/${chunks.length}]`;\n try {\n const systemPrompt =\n 'You are an expert content analyst processing a SECTION of a larger document. ' +\n 'Extract and summarize the key information from THIS SECTION ONLY.\\n\\n' +\n 'Guidelines:\\n' +\n '- Do NOT write introductions or conclusions — this is a partial document\\n' +\n '- Extract ALL key facts, figures, data points, and insights\\n' +\n '- Preserve important quotes, code snippets, and specific details verbatim\\n' +\n '- Use bullet points and structured formatting\\n' +\n '- Note references to other sections without trying to resolve them';\n\n const focusLine = instruction ? `\\nFOCUS: ${instruction}\\n` : '';\n const userPrompt =\n `${chunkLabel} Extract key information from this section of ${url}:` +\n `${focusLine}\\n\\n${chunk}`;\n\n const summary = await callExtractionLlm(systemPrompt, userPrompt, 6000, config, signal);\n if (summary) {\n log.debug(\n { url, chunk: index + 1, originalChars: chunk.length, summaryChars: summary.length },\n 'Chunk summarized',\n );\n }\n return { index, summary: summary || null };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n log.warn({ url, chunk: index + 1, error: message }, 'Chunk summarization failed');\n return { index, summary: null };\n }\n });\n\n const chunkResults = await Promise.all(chunkPromises);\n\n // Collect successful summaries in order\n const summaries: string[] = [];\n for (const { index, summary } of chunkResults.sort((a, b) => a.index - b.index)) {\n if (summary) {\n summaries.push(`## Section ${index + 1}\\n${summary}`);\n }\n }\n\n if (summaries.length === 0) {\n // All chunks failed — fall back to truncated raw content\n const outputCap = Math.min(maxLength, MAX_OUTPUT_SIZE);\n const truncated = content.slice(0, outputCap);\n return truncated + '\\n\\n[...all chunk summaries failed; showing truncated raw content]';\n }\n\n log.info(\n { url, successfulChunks: summaries.length, totalChunks: chunks.length },\n 'Chunk summaries collected, synthesizing',\n );\n\n // Single chunk — return directly\n if (summaries.length === 1) {\n const result = summaries[0];\n const outputCap = Math.min(maxLength, MAX_OUTPUT_SIZE);\n return result.length > outputCap\n ? result.slice(0, outputCap) + '\\n\\n[...truncated]'\n : result;\n }\n\n // Synthesize multiple chunk summaries into one cohesive summary\n return synthesizeChunkSummaries(summaries, url, instruction, maxLength, config, signal, content.length);\n}\n\nasync function synthesizeChunkSummaries(\n summaries: string[],\n url: string,\n instruction: string | undefined,\n maxLength: number,\n config: Config | undefined,\n signal: AbortSignal | undefined,\n originalContentLength: number,\n): Promise<string> {\n const outputCap = Math.min(maxLength, MAX_OUTPUT_SIZE);\n\n const systemPrompt =\n 'You synthesize multiple section summaries into one cohesive, comprehensive summary. ' +\n 'Remove redundancy, preserve all key facts and actionable information, ' +\n 'and organize with clear markdown structure.';\n\n const focusLine = instruction ? `\\nFOCUS: ${instruction}\\n` : '';\n const combined = summaries.join('\\n\\n---\\n\\n');\n const userPrompt =\n `Synthesize these section summaries from ${url} into ONE unified markdown summary ` +\n `(under ${outputCap} characters):${focusLine}\\n\\n${combined}`;\n\n try {\n const synthesized = await callExtractionLlm(systemPrompt, userPrompt, 8000, config, signal);\n\n if (!synthesized) {\n // Synthesis LLM returned empty — fall back to concatenation\n log.warn({ url }, 'Synthesis returned empty, concatenating chunk summaries');\n const fallback = summaries.join('\\n\\n');\n return fallback.length > outputCap\n ? fallback.slice(0, outputCap) + '\\n\\n[...truncated]'\n : fallback;\n }\n\n const finalOutput = synthesized.length > outputCap\n ? synthesized.slice(0, outputCap) + '\\n\\n[...summary truncated for context management]'\n : synthesized;\n\n const compressionRatio = finalOutput.length / originalContentLength;\n log.info(\n {\n url,\n originalChars: originalContentLength,\n extractedChars: finalOutput.length,\n compressionPercent: `${(compressionRatio * 100).toFixed(1)}%`,\n },\n 'Chunked extraction complete',\n );\n\n return finalOutput;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n log.warn({ url, error: message }, 'Synthesis failed, concatenating chunk summaries');\n const fallback = summaries.join('\\n\\n');\n return fallback.length > outputCap\n ? fallback.slice(0, outputCap) + '\\n\\n[...truncated due to synthesis failure]'\n : fallback;\n }\n}\n\ntype WebExtractParams = { url: string; instruction?: string; maxLength?: number };\n\nfunction resolveConfiguredMaxLength(): number {\n return DEFAULT_WEB_EXTRACT_MAX_LENGTH;\n}\n\nexport function createWebExtractTool(deps: WebExtractDeps): AgentTool {\n return {\n name: 'web_extract',\n label: '📄 Web Extract',\n description:\n 'Extract and summarize content from a web page using LLM-powered extraction.\\n\\n' +\n 'Unlike web_fetch (which returns cleaned plain text from HTML), this tool uses a ' +\n 'dedicated model pass to produce focused markdown. Use when you need semantic extraction ' +\n 'or a specific focus (instruction), not just a single readability pass.\\n\\n' +\n 'WHEN TO USE:\\n' +\n '- Reading articles, blog posts, documentation\\n' +\n '- Extracting specific data (pricing, specs, API docs)\\n' +\n '- Summarizing long pages to save context\\n\\n' +\n 'WHEN TO USE web_fetch INSTEAD:\\n' +\n '- Inspecting raw-ish page text quickly without an extra LLM call\\n' +\n '- Downloading JSON/API responses\\n' +\n '- Debugging page rendering issues',\n parameters: WebExtractSchema,\n\n async execute(\n _toolCallId: string,\n params: any,\n signal?: AbortSignal,\n ): Promise<AgentToolResult<{ url: string; extractedLength: number }>> {\n const { url, instruction } = params as WebExtractParams;\n const cfg = deps.getConfig();\n const maxLength = (params as WebExtractParams).maxLength ?? resolveConfiguredMaxLength();\n\n try {\n // SSRF protection\n const safety = checkUrlSafety(url);\n if (!safety.safe) {\n return {\n content: [{ type: 'text', text: `Blocked: ${safety.reason}` }],\n details: { url, extractedLength: 0 },\n };\n }\n\n // Website blocklist check\n const blocked = checkWebsiteBlocklist(url, cfg?.tools?.web?.blocklist);\n if (blocked) {\n return {\n content: [{ type: 'text', text: `Blocked: ${blocked.message}` }],\n details: { url, extractedLength: 0 },\n };\n }\n\n const rawContent = await fetchPageContent(url, signal);\n const cleaned = stripHtmlBoilerplate(rawContent);\n const trimmed = cleaned.trim();\n const looksStructured = trimmed.startsWith('{') || trimmed.startsWith('[');\n if (trimmed.length < 100 && !looksStructured) {\n return {\n content: [\n { type: 'text', text: `Page at ${url} appears to have no extractable content.` },\n ],\n details: { url, extractedLength: 0 },\n };\n }\n\n const extracted = await extractWithLlm(\n cleaned,\n url,\n instruction,\n maxLength,\n cfg,\n signal,\n );\n\n return {\n content: [{ type: 'text', text: extracted }],\n details: { url, extractedLength: extracted.length },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n log.warn({ url, error: message }, 'web_extract failed');\n return {\n content: [{ type: 'text', text: `Failed to extract content from ${url}: ${message}` }],\n details: { url, extractedLength: 0 },\n };\n }\n },\n } as any;\n}\n"],"mappings":"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"}
@@ -1,10 +1,10 @@
1
- import { AgentIdentitySchema, AgentManifestSchema, AgentResponsibilitiesSchema, BoundaryPolicySchema, CapabilityPresetSchema, DreamingDeepPolicySchema, DreamingLightPolicySchema, DreamingPhasePolicySchema, DreamingPolicySchema, DreamingRemPolicySchema, ManifestModelRoleSchema, MemoryPolicySchema, ModelPolicyPatchSchema, ModelPolicySchema, PromptPolicySchema, RuntimePolicySchema, SkillPolicySchema, ToolModeSchema, ToolPolicySchema, ToolPolicySetSchema, ToolScopeSchema, WorkflowPolicySchema, WorkspacePolicySchema, init_schema } from "./schema.js";
1
+ import { AgentConfigEntrySchema, AgentIdentitySchema, AgentManifestSchema, AgentResponsibilitiesSchema, BoundaryPolicySchema, CapabilityPresetSchema, DEFAULT_CAPABILITY_PRESET_ID, DreamingDeepPolicySchema, DreamingLightPolicySchema, DreamingPhasePolicySchema, DreamingPolicySchema, DreamingRemPolicySchema, ManifestModelRoleSchema, MemoryPolicySchema, ModelPolicyPatchSchema, ModelPolicySchema, PromptPolicySchema, RuntimePolicySchema, SkillPolicySchema, ToolModeSchema, ToolPolicySchema, ToolPolicySetSchema, ToolScopeSchema, WorkflowPolicySchema, WorkspacePolicySchema, init_schema } from "./schema.js";
2
2
  import { getManifestSource, resolveEffectiveAgentManifest } from "./resolver.js";
3
3
  import { validateAgentManifest } from "./validator.js";
4
4
  import { buildAgentManifestPromptSection } from "./prompt.js";
5
5
  //#region src/agent-manifest/index.ts
6
6
  init_schema();
7
7
  //#endregion
8
- export { AgentIdentitySchema, AgentManifestSchema, AgentResponsibilitiesSchema, BoundaryPolicySchema, CapabilityPresetSchema, DreamingDeepPolicySchema, DreamingLightPolicySchema, DreamingPhasePolicySchema, DreamingPolicySchema, DreamingRemPolicySchema, ManifestModelRoleSchema, MemoryPolicySchema, ModelPolicyPatchSchema, ModelPolicySchema, PromptPolicySchema, RuntimePolicySchema, SkillPolicySchema, ToolModeSchema, ToolPolicySchema, ToolPolicySetSchema, ToolScopeSchema, WorkflowPolicySchema, WorkspacePolicySchema, buildAgentManifestPromptSection, getManifestSource, resolveEffectiveAgentManifest, validateAgentManifest };
8
+ export { AgentConfigEntrySchema, AgentIdentitySchema, AgentManifestSchema, AgentResponsibilitiesSchema, BoundaryPolicySchema, CapabilityPresetSchema, DEFAULT_CAPABILITY_PRESET_ID, DreamingDeepPolicySchema, DreamingLightPolicySchema, DreamingPhasePolicySchema, DreamingPolicySchema, DreamingRemPolicySchema, ManifestModelRoleSchema, MemoryPolicySchema, ModelPolicyPatchSchema, ModelPolicySchema, PromptPolicySchema, RuntimePolicySchema, SkillPolicySchema, ToolModeSchema, ToolPolicySchema, ToolPolicySetSchema, ToolScopeSchema, WorkflowPolicySchema, WorkspacePolicySchema, buildAgentManifestPromptSection, getManifestSource, resolveEffectiveAgentManifest, validateAgentManifest };
9
9
 
10
10
  //# sourceMappingURL=index.js.map
@@ -1,11 +1,12 @@
1
- import { type AgentManifest, type CapabilityPreset, type EffectiveAgentManifest } from './schema.js';
1
+ import { type AgentConfigEntry, type CapabilityPreset, type EffectiveAgentManifest } from './schema.js';
2
2
  export interface ResolveManifestResult {
3
3
  manifest: EffectiveAgentManifest;
4
4
  sources: Record<string, string>;
5
5
  }
6
6
  export interface ResolveManifestParams {
7
- agent: AgentManifest;
7
+ agent: AgentConfigEntry;
8
8
  presets?: Record<string, CapabilityPreset>;
9
+ defaultPresetId?: string;
9
10
  }
10
11
  export declare function resolveEffectiveAgentManifest(params: ResolveManifestParams): ResolveManifestResult;
11
12
  export declare function getManifestSource(result: ResolveManifestResult, path: string): string | undefined;