@xopcai/xopc 0.0.118 → 0.0.120

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 (503) hide show
  1. package/README.md +2 -1
  2. package/README.zh-CN.md +2 -1
  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-dzUrQoeZ.js → agent-avatar-dicebear-APTlX9Sq.js} +1 -1
  6. package/dist/gateway/static/root/assets/agents-CFW_FS_V.js +2 -0
  7. package/dist/gateway/static/root/assets/app-management-settings-panel-DTKc5HHT.js +1 -0
  8. package/dist/gateway/static/root/assets/appearance-settings-BTciFrE9.js +1 -0
  9. package/dist/gateway/static/root/assets/{apps-page--wMEL00f.js → apps-page-DT52OrbM.js} +1 -1
  10. package/dist/gateway/static/root/assets/{archive-plugin-Cki4SfU2.js → archive-plugin-CiHaUMII.js} +1 -1
  11. package/dist/gateway/static/root/assets/{attachment-load-D-KnS2Do.js → attachment-load-t4gSrNjL.js} +1 -1
  12. package/dist/gateway/static/root/assets/automation-suggestion-card-CtysjSoQ.js +1 -0
  13. package/dist/gateway/static/root/assets/automations-page-B2YvT3ES.js +1 -0
  14. package/dist/gateway/static/root/assets/{binary-plugins-D2sgvJja.js → binary-plugins-BAbSldW5.js} +1 -1
  15. package/dist/gateway/static/root/assets/{block-editor-DIq-4Gvb.js → block-editor-BNr_82xl.js} +28 -28
  16. package/dist/gateway/static/root/assets/browser-settings-page-BQEXPhDU.js +8 -0
  17. package/dist/gateway/static/root/assets/capability-presets-api-tzD702Gg.js +1 -0
  18. package/dist/gateway/static/root/assets/capability-presets-settings-panel-AGhTueAc.js +1 -0
  19. package/dist/gateway/static/root/assets/channel-recipient-api-DL_KzwGE.js +1 -0
  20. package/dist/gateway/static/root/assets/channels-settings-DRTutLlN.js +1 -0
  21. package/dist/gateway/static/root/assets/cn-BQ_8x0bT.js +1 -0
  22. package/dist/gateway/static/root/assets/connectors-page-DneJVuge.js +2 -0
  23. package/dist/gateway/static/root/assets/cron-expression-B3B1eqnr.js +2 -0
  24. package/dist/gateway/static/root/assets/dreaming-settings-BEwWA1yf.js +1 -0
  25. package/dist/gateway/static/root/assets/{extension-debug-page-DYu1KaFQ.js → extension-debug-page-Dl2kO0Sf.js} +1 -1
  26. package/dist/gateway/static/root/assets/{extension-page-B3W6QwlZ.js → extension-page-DmbLPsx_.js} +1 -1
  27. package/dist/gateway/static/root/assets/{extension-settings-page-D7VQ-cxE.js → extension-settings-page-BS2rx40e.js} +1 -1
  28. package/dist/gateway/static/root/assets/{fetch-B7xDPu-R.js → fetch-B3ME5Eri.js} +1 -1
  29. package/dist/gateway/static/root/assets/format-cron-label-arqgGlVV.js +1 -0
  30. package/dist/gateway/static/root/assets/{gateway-config-api-BTBLSeUu.js → gateway-config-api-DI9_BBTo.js} +1 -1
  31. package/dist/gateway/static/root/assets/gateway-settings-DAFI_T0W.js +1 -0
  32. package/dist/gateway/static/root/assets/{gateway-startup-retry-CYj9FWCp.js → gateway-startup-retry-CSZQTz6K.js} +1 -1
  33. package/dist/gateway/static/root/assets/goal-detail-page-DLuz4jC9.js +1 -0
  34. package/dist/gateway/static/root/assets/{goals-config-api-CQQgzPr3.js → goals-config-api-Bhzqrp2w.js} +1 -1
  35. package/dist/gateway/static/root/assets/{goals-page-BHitvJAv.js → goals-page-DTAfv9Ft.js} +2 -2
  36. package/dist/gateway/static/root/assets/goals-settings-CWN2iirQ.js +1 -0
  37. package/dist/gateway/static/root/assets/heartbeat-settings-CkkkHvOi.js +1 -0
  38. package/dist/gateway/static/root/assets/index-CnjzMw5i.js +191 -0
  39. package/dist/gateway/static/root/assets/index-E1QinYKp.css +1 -0
  40. package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-kMp08SlX.js +1 -0
  41. package/dist/gateway/static/root/assets/{locale-store-DjbfSsfA.js → locale-store-Ba72bFmP.js} +1 -1
  42. package/dist/gateway/static/root/assets/{logs-page-C2Uqvae9.js → logs-page-CDXDlS_W.js} +2 -2
  43. package/dist/gateway/static/root/assets/{markdown-split-BwOFrJ2E.js → markdown-split-Bc0w40vz.js} +1 -1
  44. package/dist/gateway/static/root/assets/{media-plugins-Cnz6Q5G6.js → media-plugins-DJ_HvgIy.js} +1 -1
  45. package/dist/gateway/static/root/assets/messages-CISb1HLv.js +3 -0
  46. package/dist/gateway/static/root/assets/models-hub-panel-BTwxMIgZ.js +2 -0
  47. package/dist/gateway/static/root/assets/note-detail-page-BBfOeB0m.js +2 -0
  48. package/dist/gateway/static/root/assets/{note-image-lightbox-B-Svpmza.js → note-image-lightbox-4zntqpZq.js} +1 -1
  49. package/dist/gateway/static/root/assets/notes-page-VocWOjoK.js +1 -0
  50. package/dist/gateway/static/root/assets/{preview-open-alternatives-Bss4TiwP.js → preview-open-alternatives-BbA7sRd_.js} +1 -1
  51. package/dist/gateway/static/root/assets/remote-access-hub-CXwjuD_Q.js +1 -0
  52. package/dist/gateway/static/root/assets/{save-bar-controls-BRNjdgKq.js → save-bar-controls-Dividh61.js} +1 -1
  53. package/dist/gateway/static/root/assets/schedule-field-pRheF2CO.js +1 -0
  54. package/dist/gateway/static/root/assets/{schema-form-Dub9sy8r.js → schema-form-D6PiPxfr.js} +1 -1
  55. package/dist/gateway/static/root/assets/sessions-page-CpcVySQK.js +1 -0
  56. package/dist/gateway/static/root/assets/settings-advanced-gate-BVy_TawT.js +1 -0
  57. package/dist/gateway/static/root/assets/{settings-collapsible-section-BeFLavGi.js → settings-collapsible-section-BMCOfuE_.js} +1 -1
  58. package/dist/gateway/static/root/assets/settings-form-section-DBkGwpIN.js +1 -0
  59. package/dist/gateway/static/root/assets/settings-page-CZpNyCWn.js +1 -0
  60. package/dist/gateway/static/root/assets/settings-page-layout-NxoW5Y57.js +1 -0
  61. package/dist/gateway/static/root/assets/setup-status-panel-MVHLLu_a.js +1 -0
  62. package/dist/gateway/static/root/assets/{share-preview-page-C8glrdqe.js → share-preview-page-B0suFCfj.js} +1 -1
  63. package/dist/gateway/static/root/assets/shares-settings-C6DBbQGo.js +1 -0
  64. package/dist/gateway/static/root/assets/skill-reload-api-6hJmhwSn.js +1 -0
  65. package/dist/gateway/static/root/assets/skills-page-Im1MA0Iw.js +2 -0
  66. package/dist/gateway/static/root/assets/system-settings-panel-CrmEnyX4.js +1 -0
  67. package/dist/gateway/static/root/assets/{theme-store-CD2wtgZV.js → theme-store-Bt6vAkSB.js} +1 -1
  68. package/dist/gateway/static/root/assets/typed-models-lib-BAsGn25g.js +1 -0
  69. package/dist/gateway/static/root/assets/{use-channel-catalog-DqlWTsDb.js → use-channel-catalog-C7vcAoGb.js} +1 -1
  70. package/dist/gateway/static/root/assets/user-profile-settings-panel-QAQWGelF.js +1 -0
  71. package/dist/gateway/static/root/assets/utils-Dl9efgnV.js +1 -0
  72. package/dist/gateway/static/root/assets/{voice-api-key-field-C73IVZeC.js → voice-api-key-field-D-6XueTv.js} +1 -1
  73. package/dist/gateway/static/root/assets/{voice-player-store-C_ArLJPM.js → voice-player-store-CMC5NOJp.js} +1 -1
  74. package/dist/gateway/static/root/assets/workflow-run-setup-panel-DVPllR4k.js +3 -0
  75. package/dist/gateway/static/root/assets/workflows-page-B94WISTW.js +18 -0
  76. package/dist/gateway/static/root/index.html +8 -9
  77. package/dist/package.js +1 -1
  78. package/dist/src/agent/agent-manager.d.ts +4 -5
  79. package/dist/src/agent/agent-manager.js +4 -8
  80. package/dist/src/agent/agent-manager.js.map +1 -1
  81. package/dist/src/agent/context/workspace-templates/AGENTS.md +3 -2
  82. package/dist/src/agent/embedded/run-for-session.js +102 -14
  83. package/dist/src/agent/embedded/run-for-session.js.map +1 -1
  84. package/dist/src/agent/fallback/candidates.d.ts +0 -8
  85. package/dist/src/agent/fallback/candidates.js.map +1 -1
  86. package/dist/src/agent/index.js +1 -1
  87. package/dist/src/agent/lifecycle/handlers/compaction.d.ts +1 -3
  88. package/dist/src/agent/lifecycle/handlers/compaction.js +1 -16
  89. package/dist/src/agent/lifecycle/handlers/compaction.js.map +1 -1
  90. package/dist/src/agent/lifecycle/progress.d.ts +0 -1
  91. package/dist/src/agent/lifecycle/progress.js +1 -6
  92. package/dist/src/agent/lifecycle/progress.js.map +1 -1
  93. package/dist/src/agent/lifecycle/timeout-wrapper.d.ts +1 -2
  94. package/dist/src/agent/lifecycle/timeout-wrapper.js +1 -7
  95. package/dist/src/agent/lifecycle/timeout-wrapper.js.map +1 -1
  96. package/dist/src/agent/memory/builtin-memory-store.d.ts +1 -1
  97. package/dist/src/agent/memory/builtin-memory-store.js +1 -1
  98. package/dist/src/agent/memory/builtin-memory-store.js.map +1 -1
  99. package/dist/src/agent/memory/builtin-provider.d.ts +2 -2
  100. package/dist/src/agent/memory/builtin-provider.js +2 -2
  101. package/dist/src/agent/memory/builtin-provider.js.map +1 -1
  102. package/dist/src/agent/memory/dreaming/light-sweep.d.ts +0 -7
  103. package/dist/src/agent/memory/dreaming/light-sweep.js +16 -277
  104. package/dist/src/agent/memory/dreaming/light-sweep.js.map +1 -1
  105. package/dist/src/agent/memory/dreaming/rem-patterns.d.ts +1 -1
  106. package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
  107. package/dist/src/agent/memory/dreaming/rem-patterns.js.map +1 -1
  108. package/dist/src/agent/memory/dreaming/short-term-store.d.ts +3 -3
  109. package/dist/src/agent/memory/dreaming/short-term-store.js +5 -5
  110. package/dist/src/agent/memory/dreaming/short-term-store.js.map +1 -1
  111. package/dist/src/agent/memory/dreaming/utils.d.ts +2 -2
  112. package/dist/src/agent/memory/dreaming/utils.js +2 -2
  113. package/dist/src/agent/memory/dreaming/utils.js.map +1 -1
  114. package/dist/src/agent/memory/index.d.ts +0 -2
  115. package/dist/src/agent/memory/index.js +1 -2
  116. package/dist/src/agent/middleware/self-verify.d.ts +0 -1
  117. package/dist/src/agent/middleware/self-verify.js +0 -2
  118. package/dist/src/agent/middleware/self-verify.js.map +1 -1
  119. package/dist/src/agent/models/index.d.ts +1 -1
  120. package/dist/src/agent/models/manager.d.ts +4 -20
  121. package/dist/src/agent/models/manager.js +9 -126
  122. package/dist/src/agent/models/manager.js.map +1 -1
  123. package/dist/src/agent/orchestration/agent-orchestrator.d.ts +0 -4
  124. package/dist/src/agent/orchestration/agent-orchestrator.js +1 -9
  125. package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
  126. package/dist/src/agent/prompt/sections/memory-skills.js +11 -10
  127. package/dist/src/agent/prompt/sections/memory-skills.js.map +1 -1
  128. package/dist/src/agent/prompt/sections/tooling.js +4 -4
  129. package/dist/src/agent/prompt/sections/tooling.js.map +1 -1
  130. package/dist/src/agent/prompt/service-prompt-builder.d.ts +0 -1
  131. package/dist/src/agent/prompt/service-prompt-builder.js +0 -12
  132. package/dist/src/agent/prompt/service-prompt-builder.js.map +1 -1
  133. package/dist/src/agent/prompt/subagent-context.js +1 -1
  134. package/dist/src/agent/prompt/subagent-context.js.map +1 -1
  135. package/dist/src/agent/prompt/types.d.ts +1 -1
  136. package/dist/src/agent/reply/startup-context.js +6 -105
  137. package/dist/src/agent/reply/startup-context.js.map +1 -1
  138. package/dist/src/agent/service.d.ts +1 -12
  139. package/dist/src/agent/service.js +3 -81
  140. package/dist/src/agent/service.js.map +1 -1
  141. package/dist/src/agent/service.types.d.ts +2 -2
  142. package/dist/src/agent/session/session-config-service.d.ts +11 -11
  143. package/dist/src/agent/session/session-config-service.js +14 -14
  144. package/dist/src/agent/session/session-config-service.js.map +1 -1
  145. package/dist/src/agent/session/session-inspector.js +3 -2
  146. package/dist/src/agent/session/session-inspector.js.map +1 -1
  147. package/dist/src/agent/starter-agents.js +12 -12
  148. package/dist/src/agent/starter-agents.js.map +1 -1
  149. package/dist/src/agent/tools/automation-tool.d.ts +14 -0
  150. package/dist/src/agent/tools/automation-tool.js +102 -0
  151. package/dist/src/agent/tools/automation-tool.js.map +1 -0
  152. package/dist/src/agent/tools/delegate-tool.js +1 -1
  153. package/dist/src/agent/tools/delegate-tool.js.map +1 -1
  154. package/dist/src/agent/tools/factory.d.ts +3 -3
  155. package/dist/src/agent/tools/factory.js +2 -2
  156. package/dist/src/agent/tools/factory.js.map +1 -1
  157. package/dist/src/agent/tools/index.d.ts +1 -1
  158. package/dist/src/agent/tools/index.js +2 -2
  159. package/dist/src/agent/tools/memory-tool.js +2 -2
  160. package/dist/src/agent/tools/memory-tool.js.map +1 -1
  161. package/dist/src/agent-manifest/resolver.d.ts +1 -0
  162. package/dist/src/agent-manifest/resolver.js +1 -0
  163. package/dist/src/agent-manifest/resolver.js.map +1 -1
  164. package/dist/src/agent-manifest/schema.d.ts +6 -0
  165. package/dist/src/agent-manifest/schema.js +12 -3
  166. package/dist/src/agent-manifest/schema.js.map +1 -1
  167. package/dist/src/ai-assist/text-assist.d.ts +1 -1
  168. package/dist/src/ai-assist/text-assist.js +56 -0
  169. package/dist/src/ai-assist/text-assist.js.map +1 -1
  170. package/dist/src/auth/profiles/store.js.map +1 -1
  171. package/dist/src/automations/api/routes.d.ts +3 -0
  172. package/dist/src/automations/api/routes.js +193 -0
  173. package/dist/src/automations/api/routes.js.map +1 -0
  174. package/dist/src/automations/domain/index.d.ts +3 -0
  175. package/dist/src/automations/domain/index.js +3 -0
  176. package/dist/src/automations/domain/schedule.d.ts +4 -0
  177. package/dist/src/automations/domain/schedule.js +37 -0
  178. package/dist/src/automations/domain/schedule.js.map +1 -0
  179. package/dist/src/automations/domain/types.d.ts +160 -0
  180. package/dist/src/automations/domain/validation.d.ts +370 -0
  181. package/dist/src/automations/domain/validation.js +149 -0
  182. package/dist/src/automations/domain/validation.js.map +1 -0
  183. package/dist/src/automations/draft/automation-draft-prompt.d.ts +26 -0
  184. package/dist/src/automations/draft/automation-draft-prompt.js +127 -0
  185. package/dist/src/automations/draft/automation-draft-prompt.js.map +1 -0
  186. package/dist/src/automations/draft/automation-draft-service.d.ts +12 -0
  187. package/dist/src/automations/draft/automation-draft-service.js +135 -0
  188. package/dist/src/automations/draft/automation-draft-service.js.map +1 -0
  189. package/dist/src/automations/draft/automation-draft-validator.d.ts +99 -0
  190. package/dist/src/automations/draft/automation-draft-validator.js +105 -0
  191. package/dist/src/automations/draft/automation-draft-validator.js.map +1 -0
  192. package/dist/src/automations/draft/automation-draft.types.d.ts +40 -0
  193. package/dist/src/automations/draft/index.d.ts +3 -0
  194. package/dist/src/automations/draft/index.js +3 -0
  195. package/dist/src/automations/index.d.ts +3 -0
  196. package/dist/src/automations/index.js +8 -0
  197. package/dist/src/automations/product-events.d.ts +5 -0
  198. package/dist/src/automations/product-events.js +21 -0
  199. package/dist/src/automations/product-events.js.map +1 -0
  200. package/dist/src/automations/service/action-executor.d.ts +9 -0
  201. package/dist/src/automations/service/action-executor.js +157 -0
  202. package/dist/src/automations/service/action-executor.js.map +1 -0
  203. package/dist/src/automations/service/automation-service.d.ts +54 -0
  204. package/dist/src/automations/service/automation-service.js +404 -0
  205. package/dist/src/automations/service/automation-service.js.map +1 -0
  206. package/dist/src/automations/service/index.d.ts +2 -0
  207. package/dist/src/automations/service/index.js +3 -0
  208. package/dist/src/automations/storage/automation-repository.d.ts +6 -0
  209. package/dist/src/automations/storage/automation-repository.js +61 -0
  210. package/dist/src/automations/storage/automation-repository.js.map +1 -0
  211. package/dist/src/automations/storage/automation-run-repository.d.ts +17 -0
  212. package/dist/src/automations/storage/automation-run-repository.js +130 -0
  213. package/dist/src/automations/storage/automation-run-repository.js.map +1 -0
  214. package/dist/src/automations/storage/index.d.ts +2 -0
  215. package/dist/src/automations/storage/index.js +3 -0
  216. package/dist/src/channels/plugin-types.d.ts +3 -0
  217. package/dist/src/chat-commands/builtins/thinking.js.map +1 -1
  218. package/dist/src/cli/command-catalog.js +0 -39
  219. package/dist/src/cli/command-catalog.js.map +1 -1
  220. package/dist/src/cli/command-loaders.js +0 -1
  221. package/dist/src/cli/command-loaders.js.map +1 -1
  222. package/dist/src/cli/command-manifest.js +0 -4
  223. package/dist/src/cli/command-manifest.js.map +1 -1
  224. package/dist/src/cli/commands/agents.js +7 -6
  225. package/dist/src/cli/commands/agents.js.map +1 -1
  226. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  227. package/dist/src/cli/commands/doctor/checks/{cron-health.d.ts → migrations.d.ts} +1 -1
  228. package/dist/src/cli/commands/doctor/checks/migrations.js +40 -0
  229. package/dist/src/cli/commands/doctor/checks/migrations.js.map +1 -0
  230. package/dist/src/cli/commands/doctor/flow.js +2 -2
  231. package/dist/src/cli/commands/doctor/flow.js.map +1 -1
  232. package/dist/src/cli/commands/gateway/call.js +2 -2
  233. package/dist/src/cli/commands/gateway/call.js.map +1 -1
  234. package/dist/src/cli/commands/gateway/run-foreground.js +4 -2
  235. package/dist/src/cli/commands/gateway/run-foreground.js.map +1 -1
  236. package/dist/src/cli/commands/init.js +27 -9
  237. package/dist/src/cli/commands/init.js.map +1 -1
  238. package/dist/src/cli/commands/tui.js +24 -2
  239. package/dist/src/cli/commands/tui.js.map +1 -1
  240. package/dist/src/cli/templates.js +3 -2
  241. package/dist/src/cli/templates.js.map +1 -1
  242. package/dist/src/cli/utils/init-workspace-core.js +2 -0
  243. package/dist/src/cli/utils/init-workspace-core.js.map +1 -1
  244. package/dist/src/commands/agents.config.d.ts +9 -0
  245. package/dist/src/commands/agents.config.js +40 -2
  246. package/dist/src/commands/agents.config.js.map +1 -1
  247. package/dist/src/config/agent-profile.js +4 -3
  248. package/dist/src/config/agent-profile.js.map +1 -1
  249. package/dist/src/config/agent-typed-models.js.map +1 -1
  250. package/dist/src/config/config-surface.d.ts +3 -0
  251. package/dist/src/config/index.d.ts +0 -1
  252. package/dist/src/config/index.js +3 -3
  253. package/dist/src/config/index.js.map +1 -1
  254. package/dist/src/config/paths.d.ts +0 -9
  255. package/dist/src/config/paths.js +1 -14
  256. package/dist/src/config/paths.js.map +1 -1
  257. package/dist/src/config/profile.js +0 -1
  258. package/dist/src/config/profile.js.map +1 -1
  259. package/dist/src/config/rules.js +0 -5
  260. package/dist/src/config/rules.js.map +1 -1
  261. package/dist/src/config/schema.d.ts +13 -12
  262. package/dist/src/config/schema.js +22 -26
  263. package/dist/src/config/schema.js.map +1 -1
  264. package/dist/src/config/web-patch.d.ts +0 -12
  265. package/dist/src/config/web-patch.js +2 -29
  266. package/dist/src/config/web-patch.js.map +1 -1
  267. package/dist/src/extensions/api.d.ts +2 -0
  268. package/dist/src/extensions/api.js +10 -0
  269. package/dist/src/extensions/api.js.map +1 -1
  270. package/dist/src/extensions/extension-registry-impl.d.ts +4 -1
  271. package/dist/src/extensions/extension-registry-impl.js +8 -0
  272. package/dist/src/extensions/extension-registry-impl.js.map +1 -1
  273. package/dist/src/extensions/loader.d.ts +3 -3
  274. package/dist/src/extensions/loader.js +3 -3
  275. package/dist/src/extensions/loader.js.map +1 -1
  276. package/dist/src/extensions/sdk/testing.d.ts +2 -0
  277. package/dist/src/extensions/sdk/testing.js +7 -0
  278. package/dist/src/extensions/sdk/testing.js.map +1 -1
  279. package/dist/src/extensions/types/core.d.ts +10 -1
  280. package/dist/src/extensions/types/index.d.ts +1 -1
  281. package/dist/src/extensions/types/{phase4.d.ts → registration.d.ts} +2 -2
  282. package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
  283. package/dist/src/gateway/agent-builtin-tools.js +1 -1
  284. package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
  285. package/dist/src/gateway/agents-admin.d.ts +1 -0
  286. package/dist/src/gateway/agents-admin.js +13 -15
  287. package/dist/src/gateway/agents-admin.js.map +1 -1
  288. package/dist/src/gateway/capability-presets-admin.d.ts +1 -0
  289. package/dist/src/gateway/capability-presets-admin.js +19 -10
  290. package/dist/src/gateway/capability-presets-admin.js.map +1 -1
  291. package/dist/src/gateway/dreaming-automation-reconciler.d.ts +11 -0
  292. package/dist/src/gateway/dreaming-automation-reconciler.js +105 -0
  293. package/dist/src/gateway/dreaming-automation-reconciler.js.map +1 -0
  294. package/dist/src/gateway/heartbeat/service.d.ts +0 -2
  295. package/dist/src/gateway/heartbeat/service.js +0 -7
  296. package/dist/src/gateway/heartbeat/service.js.map +1 -1
  297. package/dist/src/gateway/hono/lib/config-payload.d.ts +5 -6
  298. package/dist/src/gateway/hono/lib/config-payload.js +2 -2
  299. package/dist/src/gateway/hono/lib/config-payload.js.map +1 -1
  300. package/dist/src/gateway/hono/routes/agents.js.map +1 -1
  301. package/dist/src/gateway/hono/routes/config-patch/misc.d.ts +1 -1
  302. package/dist/src/gateway/hono/routes/config-patch/misc.js +15 -6
  303. package/dist/src/gateway/hono/routes/config-patch/misc.js.map +1 -1
  304. package/dist/src/gateway/hono/routes/dreaming.js +27 -37
  305. package/dist/src/gateway/hono/routes/dreaming.js.map +1 -1
  306. package/dist/src/gateway/hono/routes/home.d.ts +2 -2
  307. package/dist/src/gateway/hono/routes/home.js +34 -25
  308. package/dist/src/gateway/hono/routes/home.js.map +1 -1
  309. package/dist/src/gateway/hono/routes/lazy-bundles.js +10 -6
  310. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  311. package/dist/src/gateway/hono/routes/migrations.d.ts +3 -0
  312. package/dist/src/gateway/hono/routes/migrations.js +35 -0
  313. package/dist/src/gateway/hono/routes/migrations.js.map +1 -0
  314. package/dist/src/gateway/hono/routes/public-gateway.js +2 -1
  315. package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
  316. package/dist/src/gateway/hono/routes/sessions.js +14 -0
  317. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  318. package/dist/src/gateway/run-loop.js +2 -0
  319. package/dist/src/gateway/run-loop.js.map +1 -1
  320. package/dist/src/gateway/security/operator-scopes.js +5 -1
  321. package/dist/src/gateway/security/operator-scopes.js.map +1 -1
  322. package/dist/src/gateway/service/config-coordinator.d.ts +4 -4
  323. package/dist/src/gateway/service/config-coordinator.js +14 -8
  324. package/dist/src/gateway/service/config-coordinator.js.map +1 -1
  325. package/dist/src/gateway/service/sessions-api.d.ts +2 -1
  326. package/dist/src/gateway/service/sessions-api.js +6 -1
  327. package/dist/src/gateway/service/sessions-api.js.map +1 -1
  328. package/dist/src/gateway/service.d.ts +7 -3
  329. package/dist/src/gateway/service.js +61 -27
  330. package/dist/src/gateway/service.js.map +1 -1
  331. package/dist/src/goals/goal-service.js +45 -4
  332. package/dist/src/goals/goal-service.js.map +1 -1
  333. package/dist/src/migrations/config/default-capability-preset.d.ts +10 -0
  334. package/dist/src/migrations/config/default-capability-preset.js +135 -0
  335. package/dist/src/migrations/config/default-capability-preset.js.map +1 -0
  336. package/dist/src/migrations/index.d.ts +4 -0
  337. package/dist/src/migrations/index.js +4 -0
  338. package/dist/src/migrations/registry.d.ts +8 -0
  339. package/dist/src/migrations/registry.js +18 -0
  340. package/dist/src/migrations/registry.js.map +1 -0
  341. package/dist/src/migrations/runner.d.ts +17 -0
  342. package/dist/src/migrations/runner.js +191 -0
  343. package/dist/src/migrations/runner.js.map +1 -0
  344. package/dist/src/migrations/types.d.ts +34 -0
  345. package/dist/src/notes/service.js +24 -1
  346. package/dist/src/notes/service.js.map +1 -1
  347. package/dist/src/providers/model-registry.d.ts +0 -1
  348. package/dist/src/providers/model-registry.js +0 -3
  349. package/dist/src/providers/model-registry.js.map +1 -1
  350. package/dist/src/routing/resolve-tui-session-key.d.ts +3 -0
  351. package/dist/src/routing/resolve-tui-session-key.js +14 -3
  352. package/dist/src/routing/resolve-tui-session-key.js.map +1 -1
  353. package/dist/src/session/transcript-outline.d.ts +47 -0
  354. package/dist/src/session/transcript-outline.js +481 -0
  355. package/dist/src/session/transcript-outline.js.map +1 -0
  356. package/dist/src/storage/sqlite/index.d.ts +0 -2
  357. package/dist/src/storage/sqlite/index.js +1 -3
  358. package/dist/src/storage/sqlite/migrations/016_automations.sql +47 -0
  359. package/dist/src/storage/sqlite/migrations/017_automation_run_events.sql +14 -0
  360. package/dist/src/storage/sqlite/migrations/018_automation_safety.sql +1 -0
  361. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  362. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  363. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  364. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  365. package/dist/src/storage/sqlite/row-mappers.js +3 -1
  366. package/dist/src/storage/sqlite/row-mappers.js.map +1 -1
  367. package/dist/src/storage/sqlite/schema.js +1 -1
  368. package/dist/src/storage/sqlite/schema.sql +56 -46
  369. package/dist/src/tui/backends/embedded-backend.d.ts +6 -0
  370. package/dist/src/tui/backends/embedded-backend.js +19 -9
  371. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  372. package/dist/src/tui/backends/gateway-sse-backend.d.ts +3 -0
  373. package/dist/src/tui/backends/gateway-sse-backend.js +14 -0
  374. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
  375. package/dist/src/tui/tui-backend.d.ts +4 -0
  376. package/dist/src/tui/tui-commands.d.ts +5 -0
  377. package/dist/src/tui/tui-commands.js +43 -0
  378. package/dist/src/tui/tui-commands.js.map +1 -1
  379. package/dist/src/tui/tui-transcript-tree.d.ts +1 -1
  380. package/dist/src/tui/tui-transcript-tree.js +17 -257
  381. package/dist/src/tui/tui-transcript-tree.js.map +1 -1
  382. package/dist/src/tui/tui-types.d.ts +2 -0
  383. package/dist/src/tui/tui-types.js.map +1 -1
  384. package/dist/src/tui/tui.js +7 -2
  385. package/dist/src/tui/tui.js.map +1 -1
  386. package/dist/src/workflows/domain/index.js +1 -1
  387. package/dist/src/workflows/domain/run.d.ts +7 -7
  388. package/dist/src/workflows/domain/run.js.map +1 -1
  389. package/dist/src/workflows/index.js +2 -2
  390. package/dist/src/workflows/service/workflow-run-service.d.ts +2 -0
  391. package/dist/src/workflows/service/workflow-run-service.js +31 -9
  392. package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
  393. package/dist/src/workflows/service/workflow-run-service.types.d.ts +1 -1
  394. package/package.json +4 -2
  395. package/dist/gateway/static/root/assets/agents-BCxZbkOw.js +0 -2
  396. package/dist/gateway/static/root/assets/app-management-settings-panel-bxYJVJpx.js +0 -1
  397. package/dist/gateway/static/root/assets/appearance-settings-BCIykXlI.js +0 -1
  398. package/dist/gateway/static/root/assets/browser-settings-page-DRhKdn81.js +0 -8
  399. package/dist/gateway/static/root/assets/capability-presets-api-CorhiHlg.js +0 -1
  400. package/dist/gateway/static/root/assets/capability-presets-settings-panel-qFx9Jtce.js +0 -1
  401. package/dist/gateway/static/root/assets/channels-settings-BAwAkqIA.js +0 -1
  402. package/dist/gateway/static/root/assets/cn-JdwAhgNB.js +0 -1
  403. package/dist/gateway/static/root/assets/connectors-page-BQLcfitM.js +0 -2
  404. package/dist/gateway/static/root/assets/createLucideIcon-DPHK1VkS.js +0 -1
  405. package/dist/gateway/static/root/assets/cron-api-qGwmL-24.js +0 -1
  406. package/dist/gateway/static/root/assets/cron-dreaming-jobs-nCMWCf0p.js +0 -1
  407. package/dist/gateway/static/root/assets/cron-page-BBWMAj8-.js +0 -3
  408. package/dist/gateway/static/root/assets/dreaming-settings-Dw89Obml.js +0 -1
  409. package/dist/gateway/static/root/assets/format-cron-label-BsotZDO5.js +0 -2
  410. package/dist/gateway/static/root/assets/format-interval-label-BzxafKxR.js +0 -1
  411. package/dist/gateway/static/root/assets/gateway-settings-Cmwy58rS.js +0 -1
  412. package/dist/gateway/static/root/assets/goal-detail-page-l7mu5GWX.js +0 -1
  413. package/dist/gateway/static/root/assets/goals-settings-CYVY4NGp.js +0 -1
  414. package/dist/gateway/static/root/assets/heartbeat-config-api-DriO_CN9.js +0 -1
  415. package/dist/gateway/static/root/assets/heartbeat-settings-CUQTKxg7.js +0 -1
  416. package/dist/gateway/static/root/assets/index-C17Tsd_b.css +0 -1
  417. package/dist/gateway/static/root/assets/index-Cc41r16l.js +0 -191
  418. package/dist/gateway/static/root/assets/interval-presets-DFPCKW_W.js +0 -1
  419. package/dist/gateway/static/root/assets/keyboard-shortcuts-settings-CcVcqstx.js +0 -1
  420. package/dist/gateway/static/root/assets/messages-B1iSKjiw.js +0 -3
  421. package/dist/gateway/static/root/assets/models-hub-panel-BfLfcta8.js +0 -2
  422. package/dist/gateway/static/root/assets/note-detail-page-BjwOQBfT.js +0 -2
  423. package/dist/gateway/static/root/assets/notes-page-DktdBiwB.js +0 -1
  424. package/dist/gateway/static/root/assets/remote-access-hub-rfVx96fq.js +0 -1
  425. package/dist/gateway/static/root/assets/schedule-field-CUCKODde.js +0 -1
  426. package/dist/gateway/static/root/assets/sessions-page-DbtMFuuH.js +0 -1
  427. package/dist/gateway/static/root/assets/settings-advanced-gate-5re5gAnP.js +0 -1
  428. package/dist/gateway/static/root/assets/settings-form-section-dmyaPOTG.js +0 -1
  429. package/dist/gateway/static/root/assets/settings-page-D-WMyLlj.js +0 -1
  430. package/dist/gateway/static/root/assets/setup-status-panel-Cl-DEJAU.js +0 -1
  431. package/dist/gateway/static/root/assets/shares-settings-CyPVvbLt.js +0 -1
  432. package/dist/gateway/static/root/assets/skill-reload-api-CleTH1oz.js +0 -1
  433. package/dist/gateway/static/root/assets/skills-page-Ck8hdXqR.js +0 -2
  434. package/dist/gateway/static/root/assets/system-settings-panel-DI-LN_8G.js +0 -1
  435. package/dist/gateway/static/root/assets/typed-models-lib-kX9duch4.js +0 -1
  436. package/dist/gateway/static/root/assets/user-profile-settings-panel-B-UFPMXy.js +0 -1
  437. package/dist/gateway/static/root/assets/utils-Dm0hKalR.js +0 -1
  438. package/dist/gateway/static/root/assets/workflows-page-BtFGp2y_.js +0 -18
  439. package/dist/src/agent/fallback/error.d.ts +0 -22
  440. package/dist/src/agent/fallback/error.js +0 -32
  441. package/dist/src/agent/fallback/error.js.map +0 -1
  442. package/dist/src/agent/fallback/index.d.ts +0 -5
  443. package/dist/src/agent/fallback/index.js +0 -9
  444. package/dist/src/agent/fallback/index.js.map +0 -1
  445. package/dist/src/agent/memory/memory-flush.d.ts +0 -27
  446. package/dist/src/agent/memory/memory-flush.js +0 -124
  447. package/dist/src/agent/memory/memory-flush.js.map +0 -1
  448. package/dist/src/agent/orchestration/run-agent-turn-with-fallbacks.d.ts +0 -29
  449. package/dist/src/agent/orchestration/run-agent-turn-with-fallbacks.js +0 -76
  450. package/dist/src/agent/orchestration/run-agent-turn-with-fallbacks.js.map +0 -1
  451. package/dist/src/agent/service/reconcile-dreaming-cron.d.ts +0 -6
  452. package/dist/src/agent/service/reconcile-dreaming-cron.js +0 -84
  453. package/dist/src/agent/service/reconcile-dreaming-cron.js.map +0 -1
  454. package/dist/src/agent/tools/cronjob-tool.d.ts +0 -29
  455. package/dist/src/agent/tools/cronjob-tool.js +0 -274
  456. package/dist/src/agent/tools/cronjob-tool.js.map +0 -1
  457. package/dist/src/cli/commands/cron-cli.d.ts +0 -2
  458. package/dist/src/cli/commands/cron-cli.js +0 -15
  459. package/dist/src/cli/commands/cron-cli.js.map +0 -1
  460. package/dist/src/cli/commands/cron.d.ts +0 -4
  461. package/dist/src/cli/commands/cron.js +0 -188
  462. package/dist/src/cli/commands/cron.js.map +0 -1
  463. package/dist/src/cli/commands/doctor/checks/cron-health.js +0 -35
  464. package/dist/src/cli/commands/doctor/checks/cron-health.js.map +0 -1
  465. package/dist/src/cron/execution-types.d.ts +0 -42
  466. package/dist/src/cron/executor.d.ts +0 -67
  467. package/dist/src/cron/executor.js +0 -669
  468. package/dist/src/cron/executor.js.map +0 -1
  469. package/dist/src/cron/index.d.ts +0 -7
  470. package/dist/src/cron/index.js +0 -7
  471. package/dist/src/cron/job-content.d.ts +0 -5
  472. package/dist/src/cron/job-content.js +0 -15
  473. package/dist/src/cron/job-content.js.map +0 -1
  474. package/dist/src/cron/run-log-store.d.ts +0 -12
  475. package/dist/src/cron/run-log-store.js +0 -58
  476. package/dist/src/cron/run-log-store.js.map +0 -1
  477. package/dist/src/cron/schedule.d.ts +0 -10
  478. package/dist/src/cron/schedule.js +0 -85
  479. package/dist/src/cron/schedule.js.map +0 -1
  480. package/dist/src/cron/service.d.ts +0 -58
  481. package/dist/src/cron/service.js +0 -419
  482. package/dist/src/cron/service.js.map +0 -1
  483. package/dist/src/cron/types.d.ts +0 -183
  484. package/dist/src/cron/validation.d.ts +0 -500
  485. package/dist/src/cron/validation.js +0 -192
  486. package/dist/src/cron/validation.js.map +0 -1
  487. package/dist/src/cron/workflow-run-completion.d.ts +0 -23
  488. package/dist/src/cron/workflow-run-completion.js +0 -72
  489. package/dist/src/cron/workflow-run-completion.js.map +0 -1
  490. package/dist/src/extensions/types/phase4.js +0 -1
  491. package/dist/src/gateway/hono/routes/cron.d.ts +0 -3
  492. package/dist/src/gateway/hono/routes/cron.js +0 -109
  493. package/dist/src/gateway/hono/routes/cron.js.map +0 -1
  494. package/dist/src/storage/sqlite/cron-job-repository.d.ts +0 -6
  495. package/dist/src/storage/sqlite/cron-job-repository.js +0 -70
  496. package/dist/src/storage/sqlite/cron-job-repository.js.map +0 -1
  497. package/dist/src/storage/sqlite/cron-run-repository.d.ts +0 -5
  498. package/dist/src/storage/sqlite/cron-run-repository.js +0 -96
  499. package/dist/src/storage/sqlite/cron-run-repository.js.map +0 -1
  500. /package/dist/src/{cron → automations/domain}/types.js +0 -0
  501. /package/dist/src/{config/defaults.d.ts → automations/draft/automation-draft.types.js} +0 -0
  502. /package/dist/src/{config/defaults.js → extensions/types/registration.js} +0 -0
  503. /package/dist/src/{cron/execution-types.js → migrations/types.js} +0 -0
@@ -57,10 +57,6 @@ import "./orchestration/index.js";
57
57
  import { FeedbackCoordinator } from "./feedback/feedback-coordinator.js";
58
58
  import "./feedback/index.js";
59
59
  import { PersistentGoalService } from "./goals/persistent-goal-service.js";
60
- import { reconcileManagedDreamingCronJobs } from "./service/reconcile-dreaming-cron.js";
61
- import { cleanTrailingErrors } from "./memory/message-sanitizer.js";
62
- import { MemoryFlushService } from "./memory/memory-flush.js";
63
- import { tryApplySessionTranscriptHygiene } from "./transcript/transcript-hygiene.js";
64
60
  import { existsSync, readFileSync } from "node:fs";
65
61
  import { readFile } from "node:fs/promises";
66
62
  //#region src/agent/service.ts
@@ -90,8 +86,6 @@ var AgentService = class {
90
86
  hookRunner;
91
87
  agentId;
92
88
  workspaceDir;
93
- channelManagerRef = null;
94
- bus;
95
89
  config;
96
90
  sessionTracker;
97
91
  modelManager;
@@ -119,7 +113,7 @@ var AgentService = class {
119
113
  /**
120
114
  * Direct-turn entry points: `processDirect` (one-shot), `processDirectStreaming`
121
115
  * (SSE generator), webchat steering and SSE injection. Public so the gateway,
122
- * TUI, CLI, and cron jobs do not need to thread every call through `AgentService`.
116
+ * TUI, CLI, and automations do not need to thread every call through `AgentService`.
123
117
  */
124
118
  turnDispatcher;
125
119
  /**
@@ -153,7 +147,6 @@ var AgentService = class {
153
147
  workflowProgressBrokerHandle = null;
154
148
  feedbackCoordinator;
155
149
  agentManager;
156
- memoryFlushService;
157
150
  /**
158
151
  * Unified per-session state container (replaces six ad-hoc Maps). Owns webchat
159
152
  * publishers, last assistant text, embedded stream buffer, persistent-goal stream
@@ -169,7 +162,6 @@ var AgentService = class {
169
162
  return base ? applyConfigOverrides(base) : void 0;
170
163
  }
171
164
  constructor(bus, config) {
172
- this.bus = bus;
173
165
  this.config = config;
174
166
  this.onSessionMetadataUpdated = config.onSessionMetadataUpdated;
175
167
  this.onSessionTranscriptUpdated = config.onSessionTranscriptUpdated;
@@ -215,7 +207,6 @@ var AgentService = class {
215
207
  progressManager: this.progressManager,
216
208
  bus
217
209
  });
218
- this.memoryFlushService = new MemoryFlushService();
219
210
  this.agentManager = new AgentManager({
220
211
  workspace: config.workspace,
221
212
  model: config.model,
@@ -230,7 +221,7 @@ var AgentService = class {
230
221
  reasoningLevel: config.reasoningLevel,
231
222
  verboseLevel: config.verboseLevel,
232
223
  gatewayClarify: config.gatewayClarify,
233
- getCronService: config.getCronService,
224
+ getAutomationService: config.getAutomationService,
234
225
  getWorkflowRunService: config.getWorkflowRunService,
235
226
  onSkillsUpdated: config.onSkillsUpdated,
236
227
  isWorkspaceTrusted: config.isWorkspaceTrusted
@@ -495,8 +486,6 @@ var AgentService = class {
495
486
  log.debug({ handlers: this.lifecycleManager.getRegisteredHandlers() }, "Lifecycle handlers initialized");
496
487
  }
497
488
  setChannelManager(channelManager) {
498
- this.modelManager.setChannelManager(channelManager);
499
- this.channelManagerRef = channelManager;
500
489
  this.inboundLoop.setChannelManager(channelManager);
501
490
  }
502
491
  /**
@@ -571,13 +560,6 @@ var AgentService = class {
571
560
  port: 0,
572
561
  host: "cli"
573
562
  });
574
- await this.reconcileDreamingCronJob().catch((err) => {
575
- const em = err instanceof Error ? err.message : String(err);
576
- log.warn({
577
- err,
578
- errorMessage: em
579
- }, `Dreaming cron reconcile failed: ${em}`);
580
- });
581
563
  log.debug("Agent service started");
582
564
  this.inboundLoop.start().catch((err) => {
583
565
  const em = err instanceof Error ? err.message : String(err);
@@ -595,17 +577,8 @@ var AgentService = class {
595
577
  log.debug("Agent service stopped");
596
578
  return Promise.resolve();
597
579
  }
598
- /**
599
- * Reconcile managed Dreaming cron job against the current effective config.
600
- * Safe to call after config saves to apply changes without restarting the process.
601
- */
602
580
  async reconcileDreamingNow() {
603
- await this.reconcileDreamingCronJob();
604
- }
605
- async reconcileDreamingCronJob() {
606
- const cron = this.config.getCronService?.();
607
- if (!cron) return;
608
- await reconcileManagedDreamingCronJobs(cron, this.effectiveAppConfig());
581
+ return Promise.resolve();
609
582
  }
610
583
  /**
611
584
  * Persist agent messages with the same sanitizer + transcript hygiene as AgentOrchestrator.
@@ -648,19 +621,6 @@ var AgentService = class {
648
621
  }
649
622
  })();
650
623
  }
651
- prepareLoadedSessionMessages(sessionKey, messages) {
652
- let out = cleanTrailingErrors(messages);
653
- try {
654
- const model = this.modelManager.getResolvedModelForSession(sessionKey);
655
- out = tryApplySessionTranscriptHygiene(out, model);
656
- } catch (err) {
657
- log.warn({
658
- err,
659
- sessionKey
660
- }, "Transcript hygiene on load skipped");
661
- }
662
- return out;
663
- }
664
624
  async resolveSessionEndpoint(sessionKey) {
665
625
  const metadata = await this.sessionStore.getMetadata(sessionKey).catch(() => null);
666
626
  return {
@@ -820,44 +780,6 @@ var AgentService = class {
820
780
  tokensBefore: result.tokensBefore,
821
781
  tokensAfter: result.tokensAfter
822
782
  }, "Session compacted");
823
- await this.checkAndFlush(sessionKey, messages, result, contextWindow);
824
- }
825
- resolveMemoryFlushConfig() {
826
- return {
827
- enabled: true,
828
- threshold: .88,
829
- softThresholdTokens: 4e3,
830
- afterCompactions: 2,
831
- maxEntryChars: 2e3,
832
- includeToolHistory: true
833
- };
834
- }
835
- async checkAndFlush(sessionKey, messages, compactionResult, contextWindow) {
836
- const cfg = this.resolveMemoryFlushConfig();
837
- if (!cfg.enabled) return;
838
- const compactedCount = (await this.sessionStore.getMetadata(sessionKey))?.compactedCount ?? 0;
839
- const exceedsThreshold = await this.sessionStore.estimateTokenUsage(sessionKey, messages) / contextWindow >= Math.max(.5, cfg.threshold - cfg.softThresholdTokens / contextWindow);
840
- const tooManyCompactions = compactedCount >= cfg.afterCompactions;
841
- if (!exceedsThreshold && !tooManyCompactions) return;
842
- const workspaceDir = this.agentManager.getResolvedWorkspaceForSession(sessionKey);
843
- const flushResult = await this.memoryFlushService.flush({
844
- workspaceDir,
845
- sessionKey,
846
- compactionResult,
847
- config: cfg
848
- });
849
- if (flushResult.flushed) {
850
- log.info({
851
- sessionKey,
852
- entryPath: flushResult.entryPath,
853
- entryLength: flushResult.entryLength
854
- }, "Memory flushed to daily notes");
855
- const currentMeta = await this.sessionStore.getMetadata(sessionKey);
856
- await this.sessionStore.updateMetadata(sessionKey, {
857
- lastFlushedAt: (/* @__PURE__ */ new Date()).toISOString(),
858
- flushCount: (currentMeta?.flushCount ?? 0) + 1
859
- });
860
- }
861
783
  }
862
784
  getContextWindow() {
863
785
  return 128e3;
@@ -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';\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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"}
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 { 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 {\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 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 automations 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\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.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 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 getAutomationService: config.getAutomationService,\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.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 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 async reconcileDreamingNow(): Promise<void> {\n return Promise.resolve();\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 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\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,7 +1,7 @@
1
1
  import type { ThinkingLevel } from '@earendil-works/pi-agent-core';
2
2
  import type { Config } from '../config/schema.js';
3
3
  import type { SessionStore } from '../session/store.js';
4
- import type { CronService } from '../cron/service.js';
4
+ import type { AutomationService } from '../automations/index.js';
5
5
  import type { ExtensionRegistryImpl as ExtensionRegistry } from '../extensions/index.js';
6
6
  import type { GatewayClarifyRequestFn } from './tools/clarify-tool.js';
7
7
  import type { ProgressStage } from './lifecycle/progress.js';
@@ -23,7 +23,7 @@ export interface AgentServiceConfig {
23
23
  gatewayClarify?: {
24
24
  requestClarification: GatewayClarifyRequestFn;
25
25
  };
26
- getCronService?: () => CronService | undefined;
26
+ getAutomationService?: () => AutomationService | undefined;
27
27
  /**
28
28
  * Gateway: reuse the gateway `SessionManager` store so web API and agent share one index + files.
29
29
  * When omitted, `AgentService` creates its own `SessionStore` (CLI / embedded).
@@ -4,9 +4,9 @@
4
4
  *
5
5
  * Previously these lived as four sibling methods on `AgentService`:
6
6
  * - `patchSessionAgentConfig` (96 lines of validation + persistence)
7
- * - `applyCronJobWorkingDirectory` (cron-specific working dir update)
8
- * - `applyCronJobModelOverride` (cron-specific model update before session creation)
9
- * - `clearCronSessionWorkingDirectoryOverride` (private helper)
7
+ * - `applyAutomationWorkingDirectory` (automation working dir update)
8
+ * - `applyAutomationModelOverride` (automation model update before session creation)
9
+ * - `clearAutomationWorkingDirectoryOverride` (private helper)
10
10
  * - `clearSessionModelOverride` (private helper)
11
11
  *
12
12
  * Extracted so `AgentService` no longer mixes "session bag" concerns with
@@ -47,24 +47,24 @@ export declare class SessionConfigService {
47
47
  */
48
48
  patch(sessionKey: string, partial: PatchSessionAgentConfigInput): Promise<PatchSessionAgentConfigResult>;
49
49
  /**
50
- * Sync persisted session working directory for an isolated cron run. Runs
51
- * may change when the job is edited; an empty/missing input clears the
50
+ * Sync persisted session working directory for an automation run. Runs may
51
+ * change when the automation is edited; an empty/missing input clears the
52
52
  * override so the session uses the effective agent default.
53
53
  */
54
- applyCronJobWorkingDirectory(sessionKey: string, workingDirectory: string | undefined): Promise<void>;
54
+ applyAutomationWorkingDirectory(sessionKey: string, workingDirectory: string | undefined): Promise<void>;
55
55
  /**
56
- * Persist a cron job model override before the direct turn creates/hydrates
56
+ * Persist an automation model override before the direct turn creates/hydrates
57
57
  * the AgentSession. Unlike the user-facing patch path, this must not call
58
- * `agentManager.setModelForSession`, because the cron session usually does
59
- * not exist yet.
58
+ * `agentManager.setModelForSession`, because the automation session usually
59
+ * does not exist yet.
60
60
  */
61
- applyCronJobModelOverride(sessionKey: string, model: string | undefined): Promise<boolean>;
61
+ applyAutomationModelOverride(sessionKey: string, model: string | undefined): Promise<boolean>;
62
62
  /**
63
63
  * Clear the session's model override (back to agent default). Used both by
64
64
  * {@link patch} and by `AgentService.resetSessionModelToAgentDefault`.
65
65
  */
66
66
  clearModelOverride(sessionKey: string): Promise<void>;
67
- private clearCronWorkingDirectoryOverride;
67
+ private clearAutomationWorkingDirectoryOverride;
68
68
  /**
69
69
  * Apply the workingDirectory branch of `patch`. Returns the next-step result
70
70
  * (`ok: true` to keep going, `ok: false, error` to short-circuit the patch).
@@ -11,9 +11,9 @@ import { mkdir } from "node:fs/promises";
11
11
  *
12
12
  * Previously these lived as four sibling methods on `AgentService`:
13
13
  * - `patchSessionAgentConfig` (96 lines of validation + persistence)
14
- * - `applyCronJobWorkingDirectory` (cron-specific working dir update)
15
- * - `applyCronJobModelOverride` (cron-specific model update before session creation)
16
- * - `clearCronSessionWorkingDirectoryOverride` (private helper)
14
+ * - `applyAutomationWorkingDirectory` (automation working dir update)
15
+ * - `applyAutomationModelOverride` (automation model update before session creation)
16
+ * - `clearAutomationWorkingDirectoryOverride` (private helper)
17
17
  * - `clearSessionModelOverride` (private helper)
18
18
  *
19
19
  * Extracted so `AgentService` no longer mixes "session bag" concerns with
@@ -79,11 +79,11 @@ var SessionConfigService = class {
79
79
  return { ok: true };
80
80
  }
81
81
  /**
82
- * Sync persisted session working directory for an isolated cron run. Runs
83
- * may change when the job is edited; an empty/missing input clears the
82
+ * Sync persisted session working directory for an automation run. Runs may
83
+ * change when the automation is edited; an empty/missing input clears the
84
84
  * override so the session uses the effective agent default.
85
85
  */
86
- async applyCronJobWorkingDirectory(sessionKey, workingDirectory) {
86
+ async applyAutomationWorkingDirectory(sessionKey, workingDirectory) {
87
87
  const raw = workingDirectory?.trim();
88
88
  if (raw) {
89
89
  const wdNorm = normalizeWorkingDirectoryInput(raw);
@@ -91,8 +91,8 @@ var SessionConfigService = class {
91
91
  log.warn({
92
92
  sessionKey,
93
93
  error: wdNorm.error
94
- }, "Cron job working directory invalid; using agent default");
95
- await this.clearCronWorkingDirectoryOverride(sessionKey);
94
+ }, "Automation working directory invalid; using agent default");
95
+ await this.clearAutomationWorkingDirectoryOverride(sessionKey);
96
96
  return;
97
97
  }
98
98
  await mkdir(wdNorm.path, { recursive: true });
@@ -100,15 +100,15 @@ var SessionConfigService = class {
100
100
  this.opts.agentManager.setSessionWorkspaceOverride(sessionKey, wdNorm.path);
101
101
  return;
102
102
  }
103
- await this.clearCronWorkingDirectoryOverride(sessionKey);
103
+ await this.clearAutomationWorkingDirectoryOverride(sessionKey);
104
104
  }
105
105
  /**
106
- * Persist a cron job model override before the direct turn creates/hydrates
106
+ * Persist an automation model override before the direct turn creates/hydrates
107
107
  * the AgentSession. Unlike the user-facing patch path, this must not call
108
- * `agentManager.setModelForSession`, because the cron session usually does
109
- * not exist yet.
108
+ * `agentManager.setModelForSession`, because the automation session usually
109
+ * does not exist yet.
110
110
  */
111
- async applyCronJobModelOverride(sessionKey, model) {
111
+ async applyAutomationModelOverride(sessionKey, model) {
112
112
  const raw = model?.trim();
113
113
  if (!raw) {
114
114
  await this.clearModelOverride(sessionKey);
@@ -131,7 +131,7 @@ var SessionConfigService = class {
131
131
  const agent = this.opts.agentManager.getAgent(sessionKey);
132
132
  if (agent) await this.opts.modelManager.applyModelForSession(agent, sessionKey);
133
133
  }
134
- async clearCronWorkingDirectoryOverride(sessionKey) {
134
+ async clearAutomationWorkingDirectoryOverride(sessionKey) {
135
135
  const existing = await this.opts.sessionConfigStore.get(sessionKey);
136
136
  if (existing?.workingDirectoryOverride) {
137
137
  const { workingDirectoryOverride: _removed, ...rest } = existing;
@@ -1 +1 @@
1
- {"version":3,"file":"session-config-service.js","names":[],"sources":["../../../../src/agent/session/session-config-service.ts"],"sourcesContent":["/**\n * SessionConfigService — owns the \"write side\" of per-session agent settings\n * (model override, thinking level, reasoning level, working directory).\n *\n * Previously these lived as four sibling methods on `AgentService`:\n * - `patchSessionAgentConfig` (96 lines of validation + persistence)\n * - `applyCronJobWorkingDirectory` (cron-specific working dir update)\n * - `applyCronJobModelOverride` (cron-specific model update before session creation)\n * - `clearCronSessionWorkingDirectoryOverride` (private helper)\n * - `clearSessionModelOverride` (private helper)\n *\n * Extracted so `AgentService` no longer mixes \"session bag\" concerns with\n * \"session config write\" concerns, and so future additions to the patch API\n * (e.g. new per-session overrides) only touch this file.\n */\n\nimport { mkdir } from 'node:fs/promises';\nimport type { ThinkingLevel } from '@earendil-works/pi-agent-core';\n\nimport type { Config } from '../../config/schema.js';\nimport { normalizeWorkingDirectoryInput } from '../../session/index.js';\nimport type { SessionConfigStore, SessionStore } from '../../session/index.js';\nimport {\n normalizeThinkLevel,\n normalizeReasoningLevel,\n normalizeVerboseLevel,\n} from '../transcript/thinking-types.js';\nimport type { AgentInstanceGateway } from '../agent-instance-gateway.js';\nimport type { ModelManager } from '../models/index.js';\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('SessionConfigService');\n\nexport interface SessionConfigServiceOptions {\n sessionStore: SessionStore;\n sessionConfigStore: SessionConfigStore;\n modelManager: ModelManager;\n agentManager: AgentInstanceGateway;\n /** Effective config snapshot accessor. */\n getConfig: () => Config | undefined;\n}\n\nexport interface PatchSessionAgentConfigInput {\n thinkingLevel?: string;\n model?: string | null;\n reasoningLevel?: string;\n verboseLevel?: string;\n workingDirectory?: string;\n}\n\nexport interface PatchSessionAgentConfigResult {\n ok: boolean;\n error?: string;\n}\n\nexport class SessionConfigService {\n private readonly opts: SessionConfigServiceOptions;\n\n constructor(opts: SessionConfigServiceOptions) {\n this.opts = opts;\n }\n\n /**\n * Apply a partial patch (model / thinking / reasoning / working directory) to\n * a session's persisted config. Returns `{ ok: false, error }` on the first\n * invalid field — earlier fields that succeeded are NOT rolled back, matching\n * the previous behaviour.\n */\n async patch(\n sessionKey: string,\n partial: PatchSessionAgentConfigInput,\n ): Promise<PatchSessionAgentConfigResult> {\n if (partial.model !== undefined) {\n if (partial.model === null || partial.model === '') {\n await this.clearModelOverride(sessionKey);\n } else {\n const ok = await this.opts.modelManager.switchModelForSession(sessionKey, partial.model);\n if (!ok) {\n return { ok: false, error: 'Invalid model' };\n }\n await this.opts.sessionConfigStore.update(sessionKey, { modelOverride: partial.model });\n this.opts.agentManager.setModelForSession(sessionKey, partial.model);\n }\n }\n\n if (partial.thinkingLevel !== undefined) {\n const normalized = normalizeThinkLevel(partial.thinkingLevel);\n if (!normalized) {\n return { ok: false, error: 'Invalid thinking level' };\n }\n await this.opts.sessionConfigStore.update(sessionKey, { thinkingLevel: normalized });\n this.opts.agentManager.setThinkingLevel(sessionKey, normalized as ThinkingLevel);\n }\n\n if (partial.reasoningLevel !== undefined) {\n const normalized = normalizeReasoningLevel(partial.reasoningLevel);\n if (!normalized) {\n return { ok: false, error: 'Invalid reasoning level' };\n }\n await this.opts.sessionConfigStore.update(sessionKey, { reasoningLevel: normalized });\n }\n\n if (partial.verboseLevel !== undefined) {\n const normalized = normalizeVerboseLevel(partial.verboseLevel);\n if (!normalized) {\n return { ok: false, error: 'Invalid verbose level' };\n }\n await this.opts.sessionConfigStore.update(sessionKey, { verboseLevel: normalized });\n }\n\n if (partial.workingDirectory !== undefined) {\n const cfg = this.opts.getConfig();\n if (!cfg) {\n return { ok: false, error: 'Config not loaded' };\n }\n const result = await this.patchWorkingDirectory(sessionKey, partial.workingDirectory);\n if (!result.ok) return result;\n }\n\n return { ok: true };\n }\n\n /**\n * Sync persisted session working directory for an isolated cron run. Runs\n * may change when the job is edited; an empty/missing input clears the\n * override so the session uses the effective agent default.\n */\n async applyCronJobWorkingDirectory(\n sessionKey: string,\n workingDirectory: string | undefined,\n ): Promise<void> {\n const raw = workingDirectory?.trim();\n if (raw) {\n const wdNorm = normalizeWorkingDirectoryInput(raw);\n if (wdNorm.ok === false) {\n log.warn(\n { sessionKey, error: wdNorm.error },\n 'Cron job working directory invalid; using agent default',\n );\n await this.clearCronWorkingDirectoryOverride(sessionKey);\n return;\n }\n await mkdir(wdNorm.path, { recursive: true });\n await this.opts.sessionConfigStore.update(sessionKey, { workingDirectoryOverride: wdNorm.path });\n this.opts.agentManager.setSessionWorkspaceOverride(sessionKey, wdNorm.path);\n return;\n }\n await this.clearCronWorkingDirectoryOverride(sessionKey);\n }\n\n /**\n * Persist a cron job model override before the direct turn creates/hydrates\n * the AgentSession. Unlike the user-facing patch path, this must not call\n * `agentManager.setModelForSession`, because the cron session usually does\n * not exist yet.\n */\n async applyCronJobModelOverride(\n sessionKey: string,\n model: string | undefined,\n ): Promise<boolean> {\n const raw = model?.trim();\n if (!raw) {\n await this.clearModelOverride(sessionKey);\n return true;\n }\n const ok = await this.opts.modelManager.switchModelForSession(sessionKey, raw);\n if (!ok) {\n await this.clearModelOverride(sessionKey);\n return false;\n }\n await this.opts.sessionConfigStore.update(sessionKey, { modelOverride: raw });\n return true;\n }\n\n /**\n * Clear the session's model override (back to agent default). Used both by\n * {@link patch} and by `AgentService.resetSessionModelToAgentDefault`.\n */\n async clearModelOverride(sessionKey: string): Promise<void> {\n this.opts.modelManager.clearSessionModelOverride(sessionKey);\n await this.opts.sessionConfigStore.update(sessionKey, { modelOverride: undefined });\n const agent = this.opts.agentManager.getAgent(sessionKey);\n if (agent) {\n await this.opts.modelManager.applyModelForSession(agent, sessionKey);\n }\n }\n\n // ── Internal helpers ───────────────────────────────────────────────────\n\n private async clearCronWorkingDirectoryOverride(sessionKey: string): Promise<void> {\n const existing = await this.opts.sessionConfigStore.get(sessionKey);\n if (existing?.workingDirectoryOverride) {\n const { workingDirectoryOverride: _removed, ...rest } = existing;\n await this.opts.sessionConfigStore.set(sessionKey, rest);\n }\n this.opts.agentManager.setSessionWorkspaceOverride(sessionKey, null);\n }\n\n /**\n * Apply the workingDirectory branch of `patch`. Returns the next-step result\n * (`ok: true` to keep going, `ok: false, error` to short-circuit the patch).\n *\n * Working directory has the strictest semantics: once a session has any\n * messages, it cannot move to a different directory — only re-setting the\n * same path is idempotent.\n */\n private async patchWorkingDirectory(\n sessionKey: string,\n workingDirectory: string,\n ): Promise<PatchSessionAgentConfigResult> {\n const existing = await this.opts.sessionConfigStore.get(sessionKey);\n const existingRaw = existing?.workingDirectoryOverride?.trim();\n const incoming = workingDirectory.trim();\n\n const priorMessages = await this.opts.sessionStore.load(sessionKey);\n\n if (priorMessages.length > 0) {\n if (!incoming) {\n return { ok: false, error: 'workingDirectory is empty' };\n }\n if (!existingRaw) {\n return {\n ok: false,\n error: 'Working directory can only be set before the first message in this conversation',\n };\n }\n const prev = normalizeWorkingDirectoryInput(existingRaw);\n const next = normalizeWorkingDirectoryInput(incoming);\n if (prev.ok && next.ok && prev.path === next.path) {\n // idempotent\n return { ok: true };\n }\n return { ok: false, error: 'Working directory is already set for this session' };\n }\n\n if (!incoming) {\n return { ok: false, error: 'workingDirectory is empty' };\n }\n const wdNorm = normalizeWorkingDirectoryInput(incoming);\n switch (wdNorm.ok) {\n case true: {\n if (existingRaw) {\n const prev = normalizeWorkingDirectoryInput(existingRaw);\n if (prev.ok && prev.path === wdNorm.path) {\n return { ok: true };\n }\n }\n await mkdir(wdNorm.path, { recursive: true });\n await this.opts.sessionConfigStore.update(sessionKey, {\n workingDirectoryOverride: wdNorm.path,\n });\n this.opts.agentManager.setSessionWorkspaceOverride(sessionKey, wdNorm.path);\n this.opts.agentManager.removeAgent(sessionKey);\n return { ok: true };\n }\n case false:\n return { ok: false, error: wdNorm.error };\n default:\n return { ok: false, error: 'Invalid working directory' };\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"session-config-service.js","names":[],"sources":["../../../../src/agent/session/session-config-service.ts"],"sourcesContent":["/**\n * SessionConfigService — owns the \"write side\" of per-session agent settings\n * (model override, thinking level, reasoning level, working directory).\n *\n * Previously these lived as four sibling methods on `AgentService`:\n * - `patchSessionAgentConfig` (96 lines of validation + persistence)\n * - `applyAutomationWorkingDirectory` (automation working dir update)\n * - `applyAutomationModelOverride` (automation model update before session creation)\n * - `clearAutomationWorkingDirectoryOverride` (private helper)\n * - `clearSessionModelOverride` (private helper)\n *\n * Extracted so `AgentService` no longer mixes \"session bag\" concerns with\n * \"session config write\" concerns, and so future additions to the patch API\n * (e.g. new per-session overrides) only touch this file.\n */\n\nimport { mkdir } from 'node:fs/promises';\nimport type { ThinkingLevel } from '@earendil-works/pi-agent-core';\n\nimport type { Config } from '../../config/schema.js';\nimport { normalizeWorkingDirectoryInput } from '../../session/index.js';\nimport type { SessionConfigStore, SessionStore } from '../../session/index.js';\nimport {\n normalizeThinkLevel,\n normalizeReasoningLevel,\n normalizeVerboseLevel,\n} from '../transcript/thinking-types.js';\nimport type { AgentInstanceGateway } from '../agent-instance-gateway.js';\nimport type { ModelManager } from '../models/index.js';\nimport { createLogger } from '../../utils/logger.js';\n\nconst log = createLogger('SessionConfigService');\n\nexport interface SessionConfigServiceOptions {\n sessionStore: SessionStore;\n sessionConfigStore: SessionConfigStore;\n modelManager: ModelManager;\n agentManager: AgentInstanceGateway;\n /** Effective config snapshot accessor. */\n getConfig: () => Config | undefined;\n}\n\nexport interface PatchSessionAgentConfigInput {\n thinkingLevel?: string;\n model?: string | null;\n reasoningLevel?: string;\n verboseLevel?: string;\n workingDirectory?: string;\n}\n\nexport interface PatchSessionAgentConfigResult {\n ok: boolean;\n error?: string;\n}\n\nexport class SessionConfigService {\n private readonly opts: SessionConfigServiceOptions;\n\n constructor(opts: SessionConfigServiceOptions) {\n this.opts = opts;\n }\n\n /**\n * Apply a partial patch (model / thinking / reasoning / working directory) to\n * a session's persisted config. Returns `{ ok: false, error }` on the first\n * invalid field — earlier fields that succeeded are NOT rolled back, matching\n * the previous behaviour.\n */\n async patch(\n sessionKey: string,\n partial: PatchSessionAgentConfigInput,\n ): Promise<PatchSessionAgentConfigResult> {\n if (partial.model !== undefined) {\n if (partial.model === null || partial.model === '') {\n await this.clearModelOverride(sessionKey);\n } else {\n const ok = await this.opts.modelManager.switchModelForSession(sessionKey, partial.model);\n if (!ok) {\n return { ok: false, error: 'Invalid model' };\n }\n await this.opts.sessionConfigStore.update(sessionKey, { modelOverride: partial.model });\n this.opts.agentManager.setModelForSession(sessionKey, partial.model);\n }\n }\n\n if (partial.thinkingLevel !== undefined) {\n const normalized = normalizeThinkLevel(partial.thinkingLevel);\n if (!normalized) {\n return { ok: false, error: 'Invalid thinking level' };\n }\n await this.opts.sessionConfigStore.update(sessionKey, { thinkingLevel: normalized });\n this.opts.agentManager.setThinkingLevel(sessionKey, normalized as ThinkingLevel);\n }\n\n if (partial.reasoningLevel !== undefined) {\n const normalized = normalizeReasoningLevel(partial.reasoningLevel);\n if (!normalized) {\n return { ok: false, error: 'Invalid reasoning level' };\n }\n await this.opts.sessionConfigStore.update(sessionKey, { reasoningLevel: normalized });\n }\n\n if (partial.verboseLevel !== undefined) {\n const normalized = normalizeVerboseLevel(partial.verboseLevel);\n if (!normalized) {\n return { ok: false, error: 'Invalid verbose level' };\n }\n await this.opts.sessionConfigStore.update(sessionKey, { verboseLevel: normalized });\n }\n\n if (partial.workingDirectory !== undefined) {\n const cfg = this.opts.getConfig();\n if (!cfg) {\n return { ok: false, error: 'Config not loaded' };\n }\n const result = await this.patchWorkingDirectory(sessionKey, partial.workingDirectory);\n if (!result.ok) return result;\n }\n\n return { ok: true };\n }\n\n /**\n * Sync persisted session working directory for an automation run. Runs may\n * change when the automation is edited; an empty/missing input clears the\n * override so the session uses the effective agent default.\n */\n async applyAutomationWorkingDirectory(\n sessionKey: string,\n workingDirectory: string | undefined,\n ): Promise<void> {\n const raw = workingDirectory?.trim();\n if (raw) {\n const wdNorm = normalizeWorkingDirectoryInput(raw);\n if (wdNorm.ok === false) {\n log.warn(\n { sessionKey, error: wdNorm.error },\n 'Automation working directory invalid; using agent default',\n );\n await this.clearAutomationWorkingDirectoryOverride(sessionKey);\n return;\n }\n await mkdir(wdNorm.path, { recursive: true });\n await this.opts.sessionConfigStore.update(sessionKey, { workingDirectoryOverride: wdNorm.path });\n this.opts.agentManager.setSessionWorkspaceOverride(sessionKey, wdNorm.path);\n return;\n }\n await this.clearAutomationWorkingDirectoryOverride(sessionKey);\n }\n\n /**\n * Persist an automation model override before the direct turn creates/hydrates\n * the AgentSession. Unlike the user-facing patch path, this must not call\n * `agentManager.setModelForSession`, because the automation session usually\n * does not exist yet.\n */\n async applyAutomationModelOverride(\n sessionKey: string,\n model: string | undefined,\n ): Promise<boolean> {\n const raw = model?.trim();\n if (!raw) {\n await this.clearModelOverride(sessionKey);\n return true;\n }\n const ok = await this.opts.modelManager.switchModelForSession(sessionKey, raw);\n if (!ok) {\n await this.clearModelOverride(sessionKey);\n return false;\n }\n await this.opts.sessionConfigStore.update(sessionKey, { modelOverride: raw });\n return true;\n }\n\n /**\n * Clear the session's model override (back to agent default). Used both by\n * {@link patch} and by `AgentService.resetSessionModelToAgentDefault`.\n */\n async clearModelOverride(sessionKey: string): Promise<void> {\n this.opts.modelManager.clearSessionModelOverride(sessionKey);\n await this.opts.sessionConfigStore.update(sessionKey, { modelOverride: undefined });\n const agent = this.opts.agentManager.getAgent(sessionKey);\n if (agent) {\n await this.opts.modelManager.applyModelForSession(agent, sessionKey);\n }\n }\n\n // ── Internal helpers ───────────────────────────────────────────────────\n\n private async clearAutomationWorkingDirectoryOverride(sessionKey: string): Promise<void> {\n const existing = await this.opts.sessionConfigStore.get(sessionKey);\n if (existing?.workingDirectoryOverride) {\n const { workingDirectoryOverride: _removed, ...rest } = existing;\n await this.opts.sessionConfigStore.set(sessionKey, rest);\n }\n this.opts.agentManager.setSessionWorkspaceOverride(sessionKey, null);\n }\n\n /**\n * Apply the workingDirectory branch of `patch`. Returns the next-step result\n * (`ok: true` to keep going, `ok: false, error` to short-circuit the patch).\n *\n * Working directory has the strictest semantics: once a session has any\n * messages, it cannot move to a different directory — only re-setting the\n * same path is idempotent.\n */\n private async patchWorkingDirectory(\n sessionKey: string,\n workingDirectory: string,\n ): Promise<PatchSessionAgentConfigResult> {\n const existing = await this.opts.sessionConfigStore.get(sessionKey);\n const existingRaw = existing?.workingDirectoryOverride?.trim();\n const incoming = workingDirectory.trim();\n\n const priorMessages = await this.opts.sessionStore.load(sessionKey);\n\n if (priorMessages.length > 0) {\n if (!incoming) {\n return { ok: false, error: 'workingDirectory is empty' };\n }\n if (!existingRaw) {\n return {\n ok: false,\n error: 'Working directory can only be set before the first message in this conversation',\n };\n }\n const prev = normalizeWorkingDirectoryInput(existingRaw);\n const next = normalizeWorkingDirectoryInput(incoming);\n if (prev.ok && next.ok && prev.path === next.path) {\n // idempotent\n return { ok: true };\n }\n return { ok: false, error: 'Working directory is already set for this session' };\n }\n\n if (!incoming) {\n return { ok: false, error: 'workingDirectory is empty' };\n }\n const wdNorm = normalizeWorkingDirectoryInput(incoming);\n switch (wdNorm.ok) {\n case true: {\n if (existingRaw) {\n const prev = normalizeWorkingDirectoryInput(existingRaw);\n if (prev.ok && prev.path === wdNorm.path) {\n return { ok: true };\n }\n }\n await mkdir(wdNorm.path, { recursive: true });\n await this.opts.sessionConfigStore.update(sessionKey, {\n workingDirectoryOverride: wdNorm.path,\n });\n this.opts.agentManager.setSessionWorkspaceOverride(sessionKey, wdNorm.path);\n this.opts.agentManager.removeAgent(sessionKey);\n return { ok: true };\n }\n case false:\n return { ok: false, error: wdNorm.error };\n default:\n return { ok: false, error: 'Invalid working directory' };\n }\n }\n}\n"],"mappings":"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"}