@xopcai/xopc 0.0.99 → 0.0.101

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 (362) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/src/account-manager.d.ts +3 -3
  3. package/dist/extensions/telegram/src/account-manager.js +8 -8
  4. package/dist/extensions/telegram/src/account-manager.js.map +1 -1
  5. package/dist/extensions/telegram/src/actions/message-actions.d.ts +4 -0
  6. package/dist/extensions/telegram/src/actions/message-actions.js +71 -0
  7. package/dist/extensions/telegram/src/actions/message-actions.js.map +1 -0
  8. package/dist/extensions/telegram/src/adapters/config-adapter.js +9 -1
  9. package/dist/extensions/telegram/src/adapters/config-adapter.js.map +1 -1
  10. package/dist/extensions/telegram/src/adapters/config-surface.js +1 -1
  11. package/dist/extensions/telegram/src/adapters/config-surface.js.map +1 -1
  12. package/dist/extensions/telegram/src/adapters/outbound-adapter.d.ts +2 -1
  13. package/dist/extensions/telegram/src/adapters/outbound-adapter.js +18 -4
  14. package/dist/extensions/telegram/src/adapters/outbound-adapter.js.map +1 -1
  15. package/dist/extensions/telegram/src/adapters/security-adapter.js +2 -1
  16. package/dist/extensions/telegram/src/adapters/security-adapter.js.map +1 -1
  17. package/dist/extensions/telegram/src/adapters/streaming-adapter.js +90 -6
  18. package/dist/extensions/telegram/src/adapters/streaming-adapter.js.map +1 -1
  19. package/dist/extensions/telegram/src/adapters/types.d.ts +9 -1
  20. package/dist/extensions/telegram/src/api-root.d.ts +4 -0
  21. package/dist/extensions/telegram/src/api-root.js +42 -0
  22. package/dist/extensions/telegram/src/api-root.js.map +1 -0
  23. package/dist/extensions/telegram/src/approval-delivery.d.ts +9 -0
  24. package/dist/extensions/telegram/src/approval-delivery.js +36 -0
  25. package/dist/extensions/telegram/src/approval-delivery.js.map +1 -0
  26. package/dist/extensions/telegram/src/approval-store.d.ts +25 -0
  27. package/dist/extensions/telegram/src/approval-store.js +61 -0
  28. package/dist/extensions/telegram/src/approval-store.js.map +1 -0
  29. package/dist/extensions/telegram/src/config-schema.d.ts +185 -35
  30. package/dist/extensions/telegram/src/config-schema.js +67 -45
  31. package/dist/extensions/telegram/src/config-schema.js.map +1 -1
  32. package/dist/extensions/telegram/src/conversation-id.d.ts +6 -0
  33. package/dist/extensions/telegram/src/conversation-id.js +18 -0
  34. package/dist/extensions/telegram/src/conversation-id.js.map +1 -0
  35. package/dist/extensions/telegram/src/doctor.d.ts +5 -0
  36. package/dist/extensions/telegram/src/doctor.js +80 -0
  37. package/dist/extensions/telegram/src/doctor.js.map +1 -0
  38. package/dist/extensions/telegram/src/exec-approval-handler.d.ts +7 -0
  39. package/dist/extensions/telegram/src/exec-approval-handler.js +36 -0
  40. package/dist/extensions/telegram/src/exec-approval-handler.js.map +1 -0
  41. package/dist/extensions/telegram/src/exec-approvals.d.ts +8 -0
  42. package/dist/extensions/telegram/src/exec-approvals.js +20 -0
  43. package/dist/extensions/telegram/src/exec-approvals.js.map +1 -0
  44. package/dist/extensions/telegram/src/focus-handler.d.ts +12 -0
  45. package/dist/extensions/telegram/src/focus-handler.js +55 -0
  46. package/dist/extensions/telegram/src/focus-handler.js.map +1 -0
  47. package/dist/extensions/telegram/src/group-config-resolver.d.ts +12 -0
  48. package/dist/extensions/telegram/src/group-config-resolver.js +17 -0
  49. package/dist/extensions/telegram/src/group-config-resolver.js.map +1 -0
  50. package/dist/extensions/telegram/src/inbound-coalescer.d.ts +15 -0
  51. package/dist/extensions/telegram/src/inbound-coalescer.js +107 -0
  52. package/dist/extensions/telegram/src/inbound-coalescer.js.map +1 -0
  53. package/dist/extensions/telegram/src/inbound-processor.d.ts +1 -0
  54. package/dist/extensions/telegram/src/inbound-processor.js +41 -4
  55. package/dist/extensions/telegram/src/inbound-processor.js.map +1 -1
  56. package/dist/extensions/telegram/src/network-errors.d.ts +2 -0
  57. package/dist/extensions/telegram/src/network-errors.js +23 -0
  58. package/dist/extensions/telegram/src/network-errors.js.map +1 -0
  59. package/dist/extensions/telegram/src/plugin.d.ts +11 -0
  60. package/dist/extensions/telegram/src/plugin.js +191 -57
  61. package/dist/extensions/telegram/src/plugin.js.map +1 -1
  62. package/dist/extensions/telegram/src/polling-session.d.ts +17 -0
  63. package/dist/extensions/telegram/src/polling-session.js +112 -0
  64. package/dist/extensions/telegram/src/polling-session.js.map +1 -0
  65. package/dist/extensions/telegram/src/proxy-fetch.d.ts +2 -0
  66. package/dist/extensions/telegram/src/proxy-fetch.js +14 -0
  67. package/dist/extensions/telegram/src/proxy-fetch.js.map +1 -0
  68. package/dist/extensions/telegram/src/reactions.d.ts +14 -0
  69. package/dist/extensions/telegram/src/reactions.js +48 -0
  70. package/dist/extensions/telegram/src/reactions.js.map +1 -0
  71. package/dist/extensions/telegram/src/reply-params.d.ts +15 -0
  72. package/dist/extensions/telegram/src/reply-params.js +28 -0
  73. package/dist/extensions/telegram/src/reply-params.js.map +1 -0
  74. package/dist/extensions/telegram/src/startup-errors.d.ts +3 -0
  75. package/dist/extensions/telegram/src/startup-errors.js +27 -0
  76. package/dist/extensions/telegram/src/startup-errors.js.map +1 -0
  77. package/dist/extensions/telegram/src/telegram-actions.d.ts +25 -0
  78. package/dist/extensions/telegram/src/telegram-actions.js +65 -0
  79. package/dist/extensions/telegram/src/telegram-actions.js.map +1 -0
  80. package/dist/extensions/telegram/src/thread-bindings.d.ts +15 -0
  81. package/dist/extensions/telegram/src/thread-bindings.js +22 -0
  82. package/dist/extensions/telegram/src/thread-bindings.js.map +1 -0
  83. package/dist/extensions/telegram/src/token-resolver.d.ts +8 -0
  84. package/dist/extensions/telegram/src/token-resolver.js +34 -0
  85. package/dist/extensions/telegram/src/token-resolver.js.map +1 -0
  86. package/dist/extensions/telegram/src/update-offset-store.d.ts +9 -0
  87. package/dist/extensions/telegram/src/update-offset-store.js +66 -0
  88. package/dist/extensions/telegram/src/update-offset-store.js.map +1 -0
  89. package/dist/extensions/telegram/xopc.extension.json +1 -1
  90. package/dist/extensions/weixin/src/api/api.js +5 -3
  91. package/dist/extensions/weixin/src/api/api.js.map +1 -1
  92. package/dist/gateway/static/root/assets/{agents-4KTK-Ku7.js → agents-u8cmUfGQ.js} +2 -2
  93. package/dist/gateway/static/root/assets/{apps-page-DkgInY2w.js → apps-page-DF8m9cUE.js} +1 -1
  94. package/dist/gateway/static/root/assets/{attachment-preview-renderer-DBAxQXb-.js → attachment-preview-renderer-CybMSIoS.js} +2 -2
  95. package/dist/gateway/static/root/assets/{attachment-process-heavy-Csq3TrrP.js → attachment-process-heavy-RnGYy9bC.js} +2 -2
  96. package/dist/gateway/static/root/assets/channels-settings-BM3doRvi.js +1 -0
  97. package/dist/gateway/static/root/assets/channels-status-swr-ah4zWEIP.js +8 -0
  98. package/dist/gateway/static/root/assets/{cron-api-BRDli2Mm.js → cron-api-DgMuL_2b.js} +1 -1
  99. package/dist/gateway/static/root/assets/{cron-page-BzEr5Za8.js → cron-page-COfUPKfq.js} +1 -1
  100. package/dist/gateway/static/root/assets/{dist-BsHh1vX0.js → dist-Dzy3Q0ZH.js} +1 -1
  101. package/dist/gateway/static/root/assets/{docx-preview-Dxj1EkCO.js → docx-preview-CajIk2FR.js} +1 -1
  102. package/dist/gateway/static/root/assets/{extension-debug-page-TWt5q_ef.js → extension-debug-page-kyxxefKh.js} +1 -1
  103. package/dist/gateway/static/root/assets/{extension-page-C3kV8ZvZ.js → extension-page-BVbCBHoI.js} +1 -1
  104. package/dist/gateway/static/root/assets/{extension-settings-page-Csy9LNC6.js → extension-settings-page-t1lb2A7v.js} +1 -1
  105. package/dist/gateway/static/root/assets/{fetch-BLLOP2CM.js → fetch-drJUxxKP.js} +1 -1
  106. package/dist/gateway/static/root/assets/{field-primitives-MXbM8HvY.js → field-primitives-6Vo1Yga4.js} +1 -1
  107. package/dist/gateway/static/root/assets/{heartbeat-config-api-CkoyqfRx.js → heartbeat-config-api-BF3psu4O.js} +1 -1
  108. package/dist/gateway/static/root/assets/index-DjNDHzwG.css +1 -0
  109. package/dist/gateway/static/root/assets/index-WAyKAilR.js +4840 -0
  110. package/dist/gateway/static/root/assets/{logs-page-B2vY41ph.js → logs-page-BTVPXcHV.js} +1 -1
  111. package/dist/gateway/static/root/assets/{note-detail-page-B7qE5pkO.js → note-detail-page-DqSAg_5Z.js} +1 -1
  112. package/dist/gateway/static/root/assets/{note-time-NBBReVR4.js → note-time-FSOjchic.js} +1 -1
  113. package/dist/gateway/static/root/assets/{notes-page-CsWYaqYM.js → notes-page-DkRX7BE2.js} +1 -1
  114. package/dist/gateway/static/root/assets/{sessions-page-BPGY3SCT.js → sessions-page-Khdw35Zk.js} +1 -1
  115. package/dist/gateway/static/root/assets/{settings-advanced-gate-DtA451RM.js → settings-advanced-gate-DspC8oEm.js} +2 -2
  116. package/dist/gateway/static/root/assets/{settings-form-section-DYR_SPlX.js → settings-form-section-DuxvUuur.js} +1 -1
  117. package/dist/gateway/static/root/assets/{settings-page-BJUgZQzN.js → settings-page-DN8JBZ8F.js} +2 -2
  118. package/dist/gateway/static/root/assets/{share-preview-page-3u8VFmdo.js → share-preview-page-UNAcbiMb.js} +2 -2
  119. package/dist/gateway/static/root/assets/{skills-page-DuyhcH4U.js → skills-page-ByYT0fXZ.js} +1 -1
  120. package/dist/gateway/static/root/assets/{theme-store-e2q2yjs4.js → theme-store-B6KXPHgK.js} +1 -1
  121. package/dist/gateway/static/root/assets/url-q28QhI7l.js +3 -0
  122. package/dist/gateway/static/root/assets/{utils-B4Vw3fnc.js → utils-DK_1H47k.js} +1 -1
  123. package/dist/gateway/static/root/assets/{voice-api-key-field-BFY67Hhp.js → voice-api-key-field-BYN2EIMs.js} +1 -1
  124. package/dist/gateway/static/root/assets/{workflow-page.utils-yUJ5jexd.js → workflow-page.utils-Bi19YNTr.js} +1 -1
  125. package/dist/gateway/static/root/assets/{workflows-page-DIMHCpNt.js → workflows-page-DP57FEKW.js} +1 -1
  126. package/dist/gateway/static/root/index.html +5 -5
  127. package/dist/package.js +1 -1
  128. package/dist/src/agent/agent-manager.d.ts +4 -0
  129. package/dist/src/agent/agent-manager.js +84 -4
  130. package/dist/src/agent/agent-manager.js.map +1 -1
  131. package/dist/src/agent/child-agent-factory.d.ts +1 -0
  132. package/dist/src/agent/child-agent-factory.js +13 -10
  133. package/dist/src/agent/child-agent-factory.js.map +1 -1
  134. package/dist/src/agent/embedded/run-for-session.d.ts +1 -0
  135. package/dist/src/agent/embedded/run-for-session.js +2 -0
  136. package/dist/src/agent/embedded/run-for-session.js.map +1 -1
  137. package/dist/src/agent/embedded/run-turn.js +1 -17
  138. package/dist/src/agent/embedded/run-turn.js.map +1 -1
  139. package/dist/src/agent/embedded/session-runner.js +3 -1
  140. package/dist/src/agent/embedded/session-runner.js.map +1 -1
  141. package/dist/src/agent/embedded/session-tool-result-guard.d.ts +1 -0
  142. package/dist/src/agent/embedded/session-tool-result-guard.js +1 -0
  143. package/dist/src/agent/embedded/session-tool-result-guard.js.map +1 -1
  144. package/dist/src/agent/inbound/attachment-pipeline.d.ts +37 -0
  145. package/dist/src/agent/inbound/attachment-pipeline.js +169 -0
  146. package/dist/src/agent/inbound/attachment-pipeline.js.map +1 -0
  147. package/dist/src/agent/inbound/turn-dispatcher.d.ts +3 -5
  148. package/dist/src/agent/inbound/turn-dispatcher.js +1 -2
  149. package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
  150. package/dist/src/agent/memory/index.d.ts +2 -0
  151. package/dist/src/agent/memory/index.js +2 -1
  152. package/dist/src/agent/memory/memory-flush.d.ts +27 -0
  153. package/dist/src/agent/memory/memory-flush.js +124 -0
  154. package/dist/src/agent/memory/memory-flush.js.map +1 -0
  155. package/dist/src/agent/messaging/outbound-coordinator.js +2 -1
  156. package/dist/src/agent/messaging/outbound-coordinator.js.map +1 -1
  157. package/dist/src/agent/messaging/stream-manager.d.ts +2 -0
  158. package/dist/src/agent/messaging/stream-manager.js +3 -0
  159. package/dist/src/agent/messaging/stream-manager.js.map +1 -1
  160. package/dist/src/agent/orchestration/agent-orchestrator.d.ts +0 -4
  161. package/dist/src/agent/orchestration/agent-orchestrator.js +40 -107
  162. package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
  163. package/dist/src/agent/prompt/contribution.d.ts +9 -0
  164. package/dist/src/agent/prompt/contribution.js +1 -0
  165. package/dist/src/agent/prompt/safety.js +0 -10
  166. package/dist/src/agent/prompt/safety.js.map +1 -1
  167. package/dist/src/agent/prompt/sanitize-for-prompt.d.ts +6 -0
  168. package/dist/src/agent/prompt/sanitize-for-prompt.js +13 -0
  169. package/dist/src/agent/prompt/sanitize-for-prompt.js.map +1 -0
  170. package/dist/src/agent/prompt/sections/behavior.d.ts +5 -0
  171. package/dist/src/agent/prompt/sections/behavior.js +50 -0
  172. package/dist/src/agent/prompt/sections/behavior.js.map +1 -0
  173. package/dist/src/agent/prompt/sections/memory-skills.d.ts +9 -0
  174. package/dist/src/agent/prompt/sections/memory-skills.js +59 -0
  175. package/dist/src/agent/prompt/sections/memory-skills.js.map +1 -0
  176. package/dist/src/agent/prompt/sections/messaging-runtime.d.ts +16 -0
  177. package/dist/src/agent/prompt/sections/messaging-runtime.js +69 -0
  178. package/dist/src/agent/prompt/sections/messaging-runtime.js.map +1 -0
  179. package/dist/src/agent/prompt/sections/project-context.d.ts +16 -0
  180. package/dist/src/agent/prompt/sections/project-context.js +58 -0
  181. package/dist/src/agent/prompt/sections/project-context.js.map +1 -0
  182. package/dist/src/agent/prompt/sections/tooling.d.ts +5 -0
  183. package/dist/src/agent/prompt/sections/tooling.js +119 -0
  184. package/dist/src/agent/prompt/sections/tooling.js.map +1 -0
  185. package/dist/src/agent/prompt/sections/workspace-runtime.d.ts +11 -0
  186. package/dist/src/agent/prompt/sections/workspace-runtime.js +32 -0
  187. package/dist/src/agent/prompt/sections/workspace-runtime.js.map +1 -0
  188. package/dist/src/agent/prompt/service-prompt-builder.d.ts +13 -0
  189. package/dist/src/agent/prompt/service-prompt-builder.js +54 -13
  190. package/dist/src/agent/prompt/service-prompt-builder.js.map +1 -1
  191. package/dist/src/agent/prompt/subagent-context.d.ts +15 -0
  192. package/dist/src/agent/prompt/subagent-context.js +68 -0
  193. package/dist/src/agent/prompt/subagent-context.js.map +1 -0
  194. package/dist/src/agent/prompt/system-prompt-params.d.ts +53 -0
  195. package/dist/src/agent/prompt/system-prompt-params.js +74 -0
  196. package/dist/src/agent/prompt/system-prompt-params.js.map +1 -0
  197. package/dist/src/agent/prompt/system-prompt.d.ts +22 -11
  198. package/dist/src/agent/prompt/system-prompt.js +79 -193
  199. package/dist/src/agent/prompt/system-prompt.js.map +1 -1
  200. package/dist/src/agent/prompt/types.d.ts +3 -0
  201. package/dist/src/agent/service/build-direct-message-content.d.ts +5 -21
  202. package/dist/src/agent/service/build-direct-message-content.js +11 -64
  203. package/dist/src/agent/service/build-direct-message-content.js.map +1 -1
  204. package/dist/src/agent/service/direct-turn-helpers.d.ts +1 -1
  205. package/dist/src/agent/service/direct-turn-helpers.js +7 -0
  206. package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
  207. package/dist/src/agent/service/process-direct-one-shot.d.ts +3 -3
  208. package/dist/src/agent/service/process-direct-one-shot.js +22 -17
  209. package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
  210. package/dist/src/agent/service/process-direct-streaming.d.ts +6 -20
  211. package/dist/src/agent/service/process-direct-streaming.js +39 -40
  212. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  213. package/dist/src/agent/service/webchat-tts.d.ts +4 -9
  214. package/dist/src/agent/service/webchat-tts.js +9 -20
  215. package/dist/src/agent/service/webchat-tts.js.map +1 -1
  216. package/dist/src/agent/service.d.ts +8 -19
  217. package/dist/src/agent/service.js +53 -11
  218. package/dist/src/agent/service.js.map +1 -1
  219. package/dist/src/agent/tools/factory.js +2 -0
  220. package/dist/src/agent/tools/factory.js.map +1 -1
  221. package/dist/src/agent/tools/index.d.ts +1 -0
  222. package/dist/src/agent/tools/index.js +2 -1
  223. package/dist/src/agent/tools/media-read-tool.d.ts +2 -0
  224. package/dist/src/agent/tools/media-read-tool.js +89 -0
  225. package/dist/src/agent/tools/media-read-tool.js.map +1 -0
  226. package/dist/src/auth/credentials.js +0 -1
  227. package/dist/src/auth/credentials.js.map +1 -1
  228. package/dist/src/channels/attachments/attachment-classifiers.d.ts +8 -0
  229. package/dist/src/channels/attachments/attachment-classifiers.js +11 -0
  230. package/dist/src/channels/attachments/attachment-classifiers.js.map +1 -0
  231. package/dist/src/channels/attachments/inbound-persist.d.ts +13 -34
  232. package/dist/src/channels/attachments/inbound-persist.js +34 -102
  233. package/dist/src/channels/attachments/inbound-persist.js.map +1 -1
  234. package/dist/src/channels/attachments/inbound-types.d.ts +13 -0
  235. package/dist/src/channels/attachments/inbound-types.js +30 -0
  236. package/dist/src/channels/attachments/inbound-types.js.map +1 -0
  237. package/dist/src/channels/attachments/outbound-tts-persist.d.ts +3 -12
  238. package/dist/src/channels/attachments/outbound-tts-persist.js +28 -48
  239. package/dist/src/channels/attachments/outbound-tts-persist.js.map +1 -1
  240. package/dist/src/channels/attachments/voice-stt-webchat.d.ts +3 -3
  241. package/dist/src/channels/attachments/voice-stt-webchat.js +8 -14
  242. package/dist/src/channels/attachments/voice-stt-webchat.js.map +1 -1
  243. package/dist/src/channels/channel-domain.d.ts +30 -2
  244. package/dist/src/channels/exec-approval-runtime.d.ts +29 -0
  245. package/dist/src/channels/exec-approval-runtime.js +30 -0
  246. package/dist/src/channels/exec-approval-runtime.js.map +1 -0
  247. package/dist/src/channels/index.js +1 -1
  248. package/dist/src/channels/plugin-types.d.ts +1 -0
  249. package/dist/src/channels/transport-types.d.ts +2 -2
  250. package/dist/src/cli/bin.d.ts +1 -0
  251. package/dist/src/cli/bin.js +1 -0
  252. package/dist/src/cli/bin.js.map +1 -1
  253. package/dist/src/cli/commands/channels.js +1 -1
  254. package/dist/src/cli/commands/doctor/checks/database-schema.d.ts +2 -0
  255. package/dist/src/cli/commands/doctor/checks/database-schema.js +62 -0
  256. package/dist/src/cli/commands/doctor/checks/database-schema.js.map +1 -0
  257. package/dist/src/cli/commands/doctor/flow.js +2 -0
  258. package/dist/src/cli/commands/doctor/flow.js.map +1 -1
  259. package/dist/src/cli/commands/init.js +2 -57
  260. package/dist/src/cli/commands/init.js.map +1 -1
  261. package/dist/src/cli/commands/onboard/channels/registry.js +1 -1
  262. package/dist/src/cli/commands/setup.js +6 -5
  263. package/dist/src/cli/commands/setup.js.map +1 -1
  264. package/dist/src/cli/utils/init-workspace-core.js +1 -2
  265. package/dist/src/cli/utils/init-workspace-core.js.map +1 -1
  266. package/dist/src/config/index.js +2 -2
  267. package/dist/src/config/paths.d.ts +0 -13
  268. package/dist/src/config/paths.js +1 -20
  269. package/dist/src/config/paths.js.map +1 -1
  270. package/dist/src/config/rules.js +1 -1
  271. package/dist/src/config/schema.d.ts +24 -0
  272. package/dist/src/config/schema.js +33 -2
  273. package/dist/src/config/schema.js.map +1 -1
  274. package/dist/src/cron/executor.js +2 -2
  275. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  276. package/dist/src/gateway/hono/oauth-async.js +36 -3
  277. package/dist/src/gateway/hono/oauth-async.js.map +1 -1
  278. package/dist/src/gateway/hono/routes/config-patch/channels.js +3 -3
  279. package/dist/src/gateway/hono/routes/config-patch/channels.js.map +1 -1
  280. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  281. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  282. package/dist/src/gateway/hono/routes/media.d.ts +3 -0
  283. package/dist/src/gateway/hono/routes/media.js +50 -0
  284. package/dist/src/gateway/hono/routes/media.js.map +1 -0
  285. package/dist/src/gateway/hono/routes/sessions.js +6 -0
  286. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  287. package/dist/src/gateway/hono/routes/workspace.js +1 -87
  288. package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
  289. package/dist/src/gateway/service/sessions-api.js +3 -0
  290. package/dist/src/gateway/service/sessions-api.js.map +1 -1
  291. package/dist/src/infra/http-proxy-env.d.ts +4 -0
  292. package/dist/src/infra/http-proxy-env.js +23 -0
  293. package/dist/src/infra/http-proxy-env.js.map +1 -0
  294. package/dist/src/media/index.d.ts +5 -0
  295. package/dist/src/media/index.js +5 -0
  296. package/dist/src/media/media-reference.d.ts +23 -0
  297. package/dist/src/media/media-reference.js +34 -0
  298. package/dist/src/media/media-reference.js.map +1 -0
  299. package/dist/src/media/paths.d.ts +5 -0
  300. package/dist/src/media/paths.js +20 -0
  301. package/dist/src/media/paths.js.map +1 -0
  302. package/dist/src/media/session-references.d.ts +8 -0
  303. package/dist/src/media/session-references.js +45 -0
  304. package/dist/src/media/session-references.js.map +1 -0
  305. package/dist/src/media/store.d.ts +19 -0
  306. package/dist/src/media/store.js +131 -0
  307. package/dist/src/media/store.js.map +1 -0
  308. package/dist/src/media/types.d.ts +34 -0
  309. package/dist/src/media/types.js +1 -0
  310. package/dist/src/media/uri.d.ts +15 -0
  311. package/dist/src/media/uri.js +51 -0
  312. package/dist/src/media/uri.js.map +1 -0
  313. package/dist/src/session/session-title.js +6 -3
  314. package/dist/src/session/session-title.js.map +1 -1
  315. package/dist/src/session/store.js +1 -1
  316. package/dist/src/session/store.js.map +1 -1
  317. package/dist/src/session/types.d.ts +12 -7
  318. package/dist/src/session/types.js.map +1 -1
  319. package/dist/src/storage/sqlite/index.d.ts +4 -1
  320. package/dist/src/storage/sqlite/index.js +6 -2
  321. package/dist/src/storage/sqlite/migrations/002_add_flush_columns.sql +3 -0
  322. package/dist/src/storage/sqlite/migrations/discover.d.ts +4 -0
  323. package/dist/src/storage/sqlite/migrations/discover.js +38 -0
  324. package/dist/src/storage/sqlite/migrations/discover.js.map +1 -0
  325. package/dist/src/storage/sqlite/migrations/discover.ts +52 -0
  326. package/dist/src/storage/sqlite/migrations/errors.d.ts +11 -0
  327. package/dist/src/storage/sqlite/migrations/errors.js +27 -0
  328. package/dist/src/storage/sqlite/migrations/errors.js.map +1 -0
  329. package/dist/src/storage/sqlite/migrations/errors.ts +32 -0
  330. package/dist/src/storage/sqlite/migrations/runner.d.ts +22 -0
  331. package/dist/src/storage/sqlite/migrations/runner.js +97 -0
  332. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -0
  333. package/dist/src/storage/sqlite/migrations/runner.ts +153 -0
  334. package/dist/src/storage/sqlite/migrations/types.d.ts +12 -0
  335. package/dist/src/storage/sqlite/migrations/types.js +1 -0
  336. package/dist/src/storage/sqlite/migrations/types.ts +13 -0
  337. package/dist/src/storage/sqlite/row-mappers.d.ts +4 -0
  338. package/dist/src/storage/sqlite/row-mappers.js +4 -0
  339. package/dist/src/storage/sqlite/row-mappers.js.map +1 -1
  340. package/dist/src/storage/sqlite/schema-version.d.ts +6 -0
  341. package/dist/src/storage/sqlite/schema-version.js +29 -0
  342. package/dist/src/storage/sqlite/schema-version.js.map +1 -0
  343. package/dist/src/storage/sqlite/schema.d.ts +5 -3
  344. package/dist/src/storage/sqlite/schema.js +24 -28
  345. package/dist/src/storage/sqlite/schema.js.map +1 -1
  346. package/dist/src/storage/sqlite/schema.sql +2 -0
  347. package/dist/src/storage/sqlite/session-metadata.js +1 -0
  348. package/dist/src/storage/sqlite/session-metadata.js.map +1 -1
  349. package/dist/src/storage/sqlite/session-repository.js +7 -3
  350. package/dist/src/storage/sqlite/session-repository.js.map +1 -1
  351. package/package.json +3 -3
  352. package/dist/gateway/static/root/assets/channels-settings-BxW1Xio4.js +0 -1
  353. package/dist/gateway/static/root/assets/channels-status-swr-Cxm6AvQO.js +0 -8
  354. package/dist/gateway/static/root/assets/index-BJDmBCSl.css +0 -1
  355. package/dist/gateway/static/root/assets/index-Cbg5TS5m.js +0 -4707
  356. package/dist/gateway/static/root/assets/url-DpFBIyN9.js +0 -3
  357. package/dist/src/cli/commands/onboard/workspace.d.ts +0 -14
  358. package/dist/src/cli/commands/onboard/workspace.js +0 -33
  359. package/dist/src/cli/commands/onboard/workspace.js.map +0 -1
  360. package/dist/src/cli/utils/workspace.d.ts +0 -35
  361. package/dist/src/cli/utils/workspace.js +0 -72
  362. package/dist/src/cli/utils/workspace.js.map +0 -1
@@ -0,0 +1,27 @@
1
+ import type { CompactionResult } from './compaction.js';
2
+ export interface MemoryFlushConfig {
3
+ enabled: boolean;
4
+ threshold: number;
5
+ softThresholdTokens: number;
6
+ afterCompactions: number;
7
+ maxEntryChars: number;
8
+ includeToolHistory: boolean;
9
+ }
10
+ export interface MemoryFlushOptions {
11
+ workspaceDir: string;
12
+ sessionKey: string;
13
+ compactionResult: CompactionResult;
14
+ config: MemoryFlushConfig;
15
+ }
16
+ export interface MemoryFlushResult {
17
+ flushed: boolean;
18
+ entryPath: string;
19
+ entryLength: number;
20
+ reason?: string;
21
+ }
22
+ export declare class MemoryFlushService {
23
+ flush(options: MemoryFlushOptions): Promise<MemoryFlushResult>;
24
+ private buildEntry;
25
+ /** YYYY-MM-DD in local time (for daily `memory/YYYY-MM-DD.md` names). */
26
+ private isoDay;
27
+ }
@@ -0,0 +1,124 @@
1
+ import { createLogger } from "../../utils/logger/index.js";
2
+ import { init_logger } from "../../utils/logger.js";
3
+ import { join } from "node:path";
4
+ import { existsSync } from "node:fs";
5
+ import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
6
+ //#region src/agent/memory/memory-flush.ts
7
+ init_logger();
8
+ const log = createLogger("MemoryFlush");
9
+ var MemoryFlushService = class {
10
+ async flush(options) {
11
+ const { workspaceDir, sessionKey, compactionResult, config } = options;
12
+ if (!config.enabled) return {
13
+ flushed: false,
14
+ entryPath: "",
15
+ entryLength: 0,
16
+ reason: "disabled"
17
+ };
18
+ const now = /* @__PURE__ */ new Date();
19
+ const day = this.isoDay(now);
20
+ const memoryDir = join(workspaceDir, "memory");
21
+ const filePath = join(memoryDir, `${day}.md`);
22
+ const entryPath = `memory/${day}.md`;
23
+ await mkdir(memoryDir, { recursive: true });
24
+ const entryText = this.buildEntry(sessionKey, compactionResult, now, config).text;
25
+ const entryLength = entryText.length;
26
+ if (entryLength > config.maxEntryChars) log.warn({
27
+ sessionKey,
28
+ entryLength,
29
+ maxEntryChars: config.maxEntryChars
30
+ }, "Flush entry exceeds maxEntryChars; truncating");
31
+ try {
32
+ if (!existsSync(filePath)) await writeFile(filePath, `# Daily Notes — ${day}\n\n` + entryText, "utf-8");
33
+ else {
34
+ const existing = await readFile(filePath, "utf-8");
35
+ if (existing.length > 5e4) log.warn({
36
+ sessionKey,
37
+ filePath,
38
+ size: existing.length
39
+ }, "Daily notes file is large; continuing append");
40
+ await appendFile(filePath, "\n" + entryText, "utf-8");
41
+ }
42
+ log.info({
43
+ sessionKey,
44
+ entryPath,
45
+ entryLength
46
+ }, "Memory flushed to daily notes");
47
+ return {
48
+ flushed: true,
49
+ entryPath,
50
+ entryLength
51
+ };
52
+ } catch (err) {
53
+ const em = err instanceof Error ? err.message : String(err);
54
+ log.error({
55
+ sessionKey,
56
+ err,
57
+ filePath
58
+ }, `Memory flush failed: ${em}`);
59
+ return {
60
+ flushed: false,
61
+ entryPath,
62
+ entryLength: 0,
63
+ reason: `write_error: ${em}`
64
+ };
65
+ }
66
+ }
67
+ buildEntry(sessionKey, result, now, config) {
68
+ const iso = now.toISOString();
69
+ const lines = [];
70
+ lines.push(`## Session Flush [${sessionKey}] ${iso}`);
71
+ lines.push("");
72
+ const summary = result.summary || "";
73
+ lines.push(`**Summary:** ${summary}`);
74
+ lines.push("");
75
+ const structured = result.structuredSummary;
76
+ if (structured) {
77
+ if (structured.userRequests && structured.userRequests.length > 0) {
78
+ lines.push("**User Requests:**");
79
+ for (const req of structured.userRequests) lines.push(`- ${req}`);
80
+ lines.push("");
81
+ }
82
+ if (structured.keyDecisions && structured.keyDecisions.length > 0) {
83
+ lines.push("**Key Decisions:**");
84
+ for (const decision of structured.keyDecisions) lines.push(`- ${decision}`);
85
+ lines.push("");
86
+ }
87
+ if (structured.filesModified && structured.filesModified.length > 0) {
88
+ lines.push("**Files Modified:**");
89
+ for (const file of structured.filesModified) lines.push(`- ${file}`);
90
+ lines.push("");
91
+ }
92
+ if (config.includeToolHistory && structured.toolCalls && structured.toolCalls.length > 0) {
93
+ lines.push("**Tool Calls:**");
94
+ for (const call of structured.toolCalls) {
95
+ const filePart = call.filePath ? ` (${call.filePath})` : "";
96
+ const status = call.success ? "success" : "failed";
97
+ lines.push(`- \`${call.toolName}\`${filePart} — ${status}`);
98
+ }
99
+ lines.push("");
100
+ }
101
+ } else if (summary) {
102
+ lines.push("**Summary (text only):**");
103
+ lines.push(summary);
104
+ lines.push("");
105
+ }
106
+ lines.push("---");
107
+ let text = lines.join("\n");
108
+ if (text.length > config.maxEntryChars) {
109
+ const headerEnd = text.indexOf("\n\n") + 2;
110
+ const header = text.slice(0, headerEnd);
111
+ const remainingBudget = config.maxEntryChars - header.length - 60;
112
+ text = header + text.slice(headerEnd).slice(0, Math.max(0, remainingBudget)) + "\n\n*(Entry truncated due to length limit)*\n---";
113
+ }
114
+ return { text };
115
+ }
116
+ /** YYYY-MM-DD in local time (for daily `memory/YYYY-MM-DD.md` names). */
117
+ isoDay(d) {
118
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
119
+ }
120
+ };
121
+ //#endregion
122
+ export { MemoryFlushService };
123
+
124
+ //# sourceMappingURL=memory-flush.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-flush.js","names":[],"sources":["../../../../src/agent/memory/memory-flush.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile, appendFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { createLogger } from '../../utils/logger.js';\nimport type { CompactionResult } from './compaction.js';\n\nconst log = createLogger('MemoryFlush');\n\nexport interface MemoryFlushConfig {\n enabled: boolean;\n threshold: number;\n softThresholdTokens: number;\n afterCompactions: number;\n maxEntryChars: number;\n includeToolHistory: boolean;\n}\n\nexport interface MemoryFlushOptions {\n workspaceDir: string;\n sessionKey: string;\n compactionResult: CompactionResult;\n config: MemoryFlushConfig;\n}\n\nexport interface MemoryFlushResult {\n flushed: boolean;\n entryPath: string;\n entryLength: number;\n reason?: string;\n}\n\nexport class MemoryFlushService {\n async flush(options: MemoryFlushOptions): Promise<MemoryFlushResult> {\n const { workspaceDir, sessionKey, compactionResult, config } = options;\n\n if (!config.enabled) {\n return { flushed: false, entryPath: '', entryLength: 0, reason: 'disabled' };\n }\n\n const now = new Date();\n const day = this.isoDay(now);\n const memoryDir = join(workspaceDir, 'memory');\n const filePath = join(memoryDir, `${day}.md`);\n const entryPath = `memory/${day}.md`;\n\n await mkdir(memoryDir, { recursive: true });\n\n const entry = this.buildEntry(sessionKey, compactionResult, now, config);\n const entryText = entry.text;\n const entryLength = entryText.length;\n\n if (entryLength > config.maxEntryChars) {\n log.warn({ sessionKey, entryLength, maxEntryChars: config.maxEntryChars }, 'Flush entry exceeds maxEntryChars; truncating');\n }\n\n try {\n const fileExists = existsSync(filePath);\n if (!fileExists) {\n const frontmatter = `# Daily Notes — ${day}\\n\\n`;\n await writeFile(filePath, frontmatter + entryText, 'utf-8');\n } else {\n const existing = await readFile(filePath, 'utf-8');\n // Warn if file is getting large (>50KB) but still append\n if (existing.length > 50_000) {\n log.warn({ sessionKey, filePath, size: existing.length }, 'Daily notes file is large; continuing append');\n }\n await appendFile(filePath, '\\n' + entryText, 'utf-8');\n }\n\n log.info({ sessionKey, entryPath, entryLength }, 'Memory flushed to daily notes');\n return { flushed: true, entryPath, entryLength };\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n log.error({ sessionKey, err, filePath }, `Memory flush failed: ${em}`);\n return { flushed: false, entryPath, entryLength: 0, reason: `write_error: ${em}` };\n }\n }\n\n private buildEntry(\n sessionKey: string,\n result: CompactionResult,\n now: Date,\n config: MemoryFlushConfig,\n ): { text: string } {\n const iso = now.toISOString();\n const lines: string[] = [];\n\n lines.push(`## Session Flush [${sessionKey}] ${iso}`);\n lines.push('');\n\n const summary = result.summary || '';\n lines.push(`**Summary:** ${summary}`);\n lines.push('');\n\n const structured = result.structuredSummary;\n if (structured) {\n if (structured.userRequests && structured.userRequests.length > 0) {\n lines.push('**User Requests:**');\n for (const req of structured.userRequests) {\n lines.push(`- ${req}`);\n }\n lines.push('');\n }\n\n if (structured.keyDecisions && structured.keyDecisions.length > 0) {\n lines.push('**Key Decisions:**');\n for (const decision of structured.keyDecisions) {\n lines.push(`- ${decision}`);\n }\n lines.push('');\n }\n\n if (structured.filesModified && structured.filesModified.length > 0) {\n lines.push('**Files Modified:**');\n for (const file of structured.filesModified) {\n lines.push(`- ${file}`);\n }\n lines.push('');\n }\n\n if (config.includeToolHistory && structured.toolCalls && structured.toolCalls.length > 0) {\n lines.push('**Tool Calls:**');\n for (const call of structured.toolCalls) {\n const filePart = call.filePath ? ` (${call.filePath})` : '';\n const status = call.success ? 'success' : 'failed';\n lines.push(`- \\`${call.toolName}\\`${filePart} — ${status}`);\n }\n lines.push('');\n }\n } else if (summary) {\n // Fallback when structuredSummary is absent\n lines.push('**Summary (text only):**');\n lines.push(summary);\n lines.push('');\n }\n\n lines.push('---');\n\n let text = lines.join('\\n');\n\n // Respect maxEntryChars by truncating from the end if needed\n if (text.length > config.maxEntryChars) {\n // Keep the header and a truncation notice\n const headerEnd = text.indexOf('\\n\\n') + 2;\n const header = text.slice(0, headerEnd);\n const remainingBudget = config.maxEntryChars - header.length - 60;\n const body = text.slice(headerEnd);\n const truncatedBody = body.slice(0, Math.max(0, remainingBudget));\n text = header + truncatedBody + '\\n\\n*(Entry truncated due to length limit)*\\n---';\n }\n\n return { text };\n }\n\n /** YYYY-MM-DD in local time (for daily `memory/YYYY-MM-DD.md` names). */\n private isoDay(d: Date): string {\n const y = d.getFullYear();\n const m = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n return `${y}-${m}-${day}`;\n }\n}\n"],"mappings":";;;;;;aAGqD;AAGrD,MAAM,MAAM,aAAa,cAAc;AAyBvC,IAAa,qBAAb,MAAgC;CAC9B,MAAM,MAAM,SAAyD;EACnE,MAAM,EAAE,cAAc,YAAY,kBAAkB,WAAW;AAE/D,MAAI,CAAC,OAAO,QACV,QAAO;GAAE,SAAS;GAAO,WAAW;GAAI,aAAa;GAAG,QAAQ;GAAY;EAG9E,MAAM,sBAAM,IAAI,MAAM;EACtB,MAAM,MAAM,KAAK,OAAO,IAAI;EAC5B,MAAM,YAAY,KAAK,cAAc,SAAS;EAC9C,MAAM,WAAW,KAAK,WAAW,GAAG,IAAI,KAAK;EAC7C,MAAM,YAAY,UAAU,IAAI;AAEhC,QAAM,MAAM,WAAW,EAAE,WAAW,MAAM,CAAC;EAG3C,MAAM,YADQ,KAAK,WAAW,YAAY,kBAAkB,KAAK,OAC1C,CAAC;EACxB,MAAM,cAAc,UAAU;AAE9B,MAAI,cAAc,OAAO,cACvB,KAAI,KAAK;GAAE;GAAY;GAAa,eAAe,OAAO;GAAe,EAAE,gDAAgD;AAG7H,MAAI;AAEF,OAAI,CADe,WAAW,SACf,CAEb,OAAM,UAAU,UAAU,mBADa,IAAI,QACH,WAAW,QAAQ;QACtD;IACL,MAAM,WAAW,MAAM,SAAS,UAAU,QAAQ;AAElD,QAAI,SAAS,SAAS,IACpB,KAAI,KAAK;KAAE;KAAY;KAAU,MAAM,SAAS;KAAQ,EAAE,+CAA+C;AAE3G,UAAM,WAAW,UAAU,OAAO,WAAW,QAAQ;;AAGvD,OAAI,KAAK;IAAE;IAAY;IAAW;IAAa,EAAE,gCAAgC;AACjF,UAAO;IAAE,SAAS;IAAM;IAAW;IAAa;WACzC,KAAK;GACZ,MAAM,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC3D,OAAI,MAAM;IAAE;IAAY;IAAK;IAAU,EAAE,wBAAwB,KAAK;AACtE,UAAO;IAAE,SAAS;IAAO;IAAW,aAAa;IAAG,QAAQ,gBAAgB;IAAM;;;CAItF,WACE,YACA,QACA,KACA,QACkB;EAClB,MAAM,MAAM,IAAI,aAAa;EAC7B,MAAM,QAAkB,EAAE;AAE1B,QAAM,KAAK,qBAAqB,WAAW,IAAI,MAAM;AACrD,QAAM,KAAK,GAAG;EAEd,MAAM,UAAU,OAAO,WAAW;AAClC,QAAM,KAAK,gBAAgB,UAAU;AACrC,QAAM,KAAK,GAAG;EAEd,MAAM,aAAa,OAAO;AAC1B,MAAI,YAAY;AACd,OAAI,WAAW,gBAAgB,WAAW,aAAa,SAAS,GAAG;AACjE,UAAM,KAAK,qBAAqB;AAChC,SAAK,MAAM,OAAO,WAAW,aAC3B,OAAM,KAAK,KAAK,MAAM;AAExB,UAAM,KAAK,GAAG;;AAGhB,OAAI,WAAW,gBAAgB,WAAW,aAAa,SAAS,GAAG;AACjE,UAAM,KAAK,qBAAqB;AAChC,SAAK,MAAM,YAAY,WAAW,aAChC,OAAM,KAAK,KAAK,WAAW;AAE7B,UAAM,KAAK,GAAG;;AAGhB,OAAI,WAAW,iBAAiB,WAAW,cAAc,SAAS,GAAG;AACnE,UAAM,KAAK,sBAAsB;AACjC,SAAK,MAAM,QAAQ,WAAW,cAC5B,OAAM,KAAK,KAAK,OAAO;AAEzB,UAAM,KAAK,GAAG;;AAGhB,OAAI,OAAO,sBAAsB,WAAW,aAAa,WAAW,UAAU,SAAS,GAAG;AACxF,UAAM,KAAK,kBAAkB;AAC7B,SAAK,MAAM,QAAQ,WAAW,WAAW;KACvC,MAAM,WAAW,KAAK,WAAW,KAAK,KAAK,SAAS,KAAK;KACzD,MAAM,SAAS,KAAK,UAAU,YAAY;AAC1C,WAAM,KAAK,OAAO,KAAK,SAAS,IAAI,SAAS,KAAK,SAAS;;AAE7D,UAAM,KAAK,GAAG;;aAEP,SAAS;AAElB,SAAM,KAAK,2BAA2B;AACtC,SAAM,KAAK,QAAQ;AACnB,SAAM,KAAK,GAAG;;AAGhB,QAAM,KAAK,MAAM;EAEjB,IAAI,OAAO,MAAM,KAAK,KAAK;AAG3B,MAAI,KAAK,SAAS,OAAO,eAAe;GAEtC,MAAM,YAAY,KAAK,QAAQ,OAAO,GAAG;GACzC,MAAM,SAAS,KAAK,MAAM,GAAG,UAAU;GACvC,MAAM,kBAAkB,OAAO,gBAAgB,OAAO,SAAS;AAG/D,UAAO,SAFM,KAAK,MAAM,UACE,CAAC,MAAM,GAAG,KAAK,IAAI,GAAG,gBAAgB,CACnC,GAAG;;AAGlC,SAAO,EAAE,MAAM;;;CAIjB,OAAe,GAAiB;AAI9B,SAAO,GAHG,EAAE,aAGD,CAAC,GAFF,OAAO,EAAE,UAAU,GAAG,EAAE,CAAC,SAAS,GAAG,IAE/B,CAAC,GADL,OAAO,EAAE,SAAS,CAAC,CAAC,SAAS,GAAG,IACrB"}
@@ -1,7 +1,7 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
- import { createTypingController } from "../lifecycle/typing.js";
4
3
  import { shouldSilence } from "../../heartbeat/tokens.js";
4
+ import { createTypingController } from "../lifecycle/typing.js";
5
5
  //#region src/agent/messaging/outbound-coordinator.ts
6
6
  init_logger();
7
7
  const log = createLogger("OutboundCoordinator");
@@ -84,6 +84,7 @@ var OutboundCoordinator = class {
84
84
  metadata: {
85
85
  accountId: msg.metadata?.accountId,
86
86
  threadId: msg.metadata?.threadId,
87
+ inboundMessageId: msg.metadata?.messageId,
87
88
  transcribedVoice: sessionContext.metadata?.transcribedVoice,
88
89
  sessionWebhook: msg.metadata?.sessionWebhook,
89
90
  conversationId: msg.metadata?.conversationId
@@ -1 +1 @@
1
- {"version":3,"file":"outbound-coordinator.js","names":[],"sources":["../../../../src/agent/messaging/outbound-coordinator.ts"],"sourcesContent":["/**\n * OutboundCoordinator — owns the post-turn outbound pipeline.\n *\n * Concentrates the responsibilities that used to be scattered across\n * `AgentService`:\n * - typing-on / typing-off lifecycle for channels that surface \"is typing…\"\n * - final assistant response delivery (silence guard + hook + bus publish)\n * - cross-cutting `webchat_turn_complete` hook + `/goal` post-turn\n * - thin pass-through for extension `message_sending` / `message_sent` hooks\n *\n * The class is intentionally framework-agnostic: it pulls everything it needs\n * via the constructor config (no direct AgentService reference), so it is\n * unit-testable and lets the parent `AgentService` shrink to a coordinator.\n */\n\nimport type { MessageBus, InboundMessage } from '../../infra/bus/index.js';\nimport type { Config } from '../../config/schema.js';\nimport type { HookHandler } from '../lifecycle/hook-handler.js';\nimport type { SessionContext } from '../session/index.js';\nimport { createTypingController, type TypingController } from '../lifecycle/typing.js';\nimport { DEFAULT_ACK_MAX_CHARS, NO_REPLY, shouldSilence } from '../../heartbeat/tokens.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { StreamManager } from './stream-manager.js';\n\nconst log = createLogger('OutboundCoordinator');\n\nexport interface OutboundCoordinatorConfig {\n bus: MessageBus;\n hookHandler: HookHandler;\n streamManager: StreamManager;\n /** Reads the effective config snapshot (honours runtime overrides). */\n getConfig: () => Config | undefined;\n /** Resolves the last visible assistant text for a session (in-memory + agent fallback). */\n getLastAssistantPlainText: (sessionKey: string) => string;\n /**\n * Run the `/goal` post-turn verdict for a completed user turn. Owned by\n * `PersistentGoalService`; OutboundCoordinator only forwards.\n */\n runPersistentGoalPostTurn: (payload: SessionTurnCompletePayload) => Promise<void>;\n}\n\nexport interface SessionTurnCompletePayload {\n sessionKey: string;\n channel: string;\n chatId: string;\n inboundUserText: string;\n assistantPlainText: string;\n aborted: boolean;\n streamError?: string;\n skipPersistentGoalPostTurn?: boolean;\n outboundMetadata?: Record<string, unknown>;\n}\n\nexport class OutboundCoordinator {\n private readonly bus: MessageBus;\n private readonly hookHandler: HookHandler;\n private readonly streamManager: StreamManager;\n private readonly getConfig: () => Config | undefined;\n private readonly getLastAssistantPlainText: (sessionKey: string) => string;\n private readonly runPersistentGoalPostTurn: OutboundCoordinatorConfig['runPersistentGoalPostTurn'];\n\n constructor(config: OutboundCoordinatorConfig) {\n this.bus = config.bus;\n this.hookHandler = config.hookHandler;\n this.streamManager = config.streamManager;\n this.getConfig = config.getConfig;\n this.getLastAssistantPlainText = config.getLastAssistantPlainText;\n this.runPersistentGoalPostTurn = config.runPersistentGoalPostTurn;\n }\n\n /**\n * Build the typing indicator controller for an inbound message. Returns\n * `null` for the CLI channel (no typing UI). Caller is responsible for\n * `start()` and `stop()` (`stop()` should run AFTER the final outbound so\n * Telegram/Weixin see `typing_off` only once the message is delivered).\n */\n createTypingControllerForInbound(msg: InboundMessage): TypingController | null {\n if (msg.channel === 'cli') {\n return null;\n }\n const meta = msg.metadata as Record<string, unknown> | undefined;\n return createTypingController({\n intervalSeconds: 5,\n onStart: async () => {\n await this.bus.publishOutbound({\n channel: msg.channel,\n chat_id: msg.chat_id,\n content: '',\n type: 'typing_on',\n metadata: {\n accountId: meta?.accountId,\n threadId: meta?.threadId,\n sessionWebhook: meta?.sessionWebhook,\n conversationId: meta?.conversationId,\n },\n });\n },\n onStop: async () => {\n await this.bus.publishOutbound({\n channel: msg.channel,\n chat_id: msg.chat_id,\n content: '',\n type: 'typing_off',\n metadata: {\n accountId: meta?.accountId,\n threadId: meta?.threadId,\n sessionWebhook: meta?.sessionWebhook,\n conversationId: meta?.conversationId,\n },\n });\n },\n });\n }\n\n /**\n * Publish the assistant's visible text as the final bus message. Honours the\n * stream-manager \"channel already streamed the final text\" hint, the heartbeat\n * silence guard, and the extension `message_sending` hook.\n */\n async sendFinalResponse(msg: InboundMessage, sessionContext: SessionContext): Promise<void> {\n if (this.streamManager.consumeSkipFinalOutbound()) {\n return;\n }\n\n const finalContent = this.getLastAssistantPlainText(sessionContext.sessionKey);\n if (!finalContent?.trim()) {\n return;\n }\n\n const ackMax = this.getConfig()?.gateway?.heartbeat?.ackMaxChars ?? DEFAULT_ACK_MAX_CHARS;\n if (shouldSilence(finalContent, ackMax) || finalContent.trim() === NO_REPLY) {\n log.debug({ sessionKey: sessionContext.sessionKey }, 'Silent reply — skipping outbound');\n return;\n }\n\n const hookResult = await this.hookHandler.runMessageSending(\n sessionContext.chatId,\n finalContent,\n sessionContext.channel,\n );\n if (!hookResult.send) {\n return;\n }\n\n await this.bus.publishOutbound({\n channel: sessionContext.channel,\n chat_id: sessionContext.chatId,\n content: hookResult.content || finalContent,\n type: 'message',\n metadata: {\n accountId: msg.metadata?.accountId,\n threadId: msg.metadata?.threadId,\n transcribedVoice: sessionContext.metadata?.transcribedVoice,\n sessionWebhook: msg.metadata?.sessionWebhook,\n conversationId: msg.metadata?.conversationId,\n },\n });\n }\n\n /**\n * Post-turn hook + `/goal` checklist evaluation. Wired into both the bus-driven\n * inbound finally block and the webchat direct-stream finally. The goal verdict\n * itself lives in `PersistentGoalService`; we just trigger the extension hook\n * here and delegate the `/goal` work.\n */\n async emitSessionTurnComplete(payload: SessionTurnCompletePayload): Promise<void> {\n await this.hookHandler.triggerWithSessionKey(payload.sessionKey, 'webchat_turn_complete', {\n sessionKey: payload.sessionKey,\n channel: payload.channel,\n chatId: payload.chatId,\n inboundUserText: payload.inboundUserText,\n assistantPlainText: payload.assistantPlainText,\n aborted: payload.aborted,\n ...(payload.streamError !== undefined ? { streamError: payload.streamError } : {}),\n });\n\n await this.runPersistentGoalPostTurn(payload);\n }\n\n /** Extension hook pass-through (Gateway ChannelManager). */\n invokeOutboundMessageSending(\n to: string,\n content: string,\n channel: string,\n ): Promise<{ send: boolean; content?: string; reason?: string }> {\n return this.hookHandler.runMessageSending(to, content, channel);\n }\n\n /** Extension hook pass-through (Gateway ChannelManager). */\n invokeOutboundMessageSent(\n to: string,\n content: string,\n success: boolean,\n error: string | undefined,\n channel: string,\n ): Promise<void> {\n return this.hookHandler.runMessageSent(to, content, success, error, channel);\n }\n}\n"],"mappings":";;;;;aAqBqD;AAGrD,MAAM,MAAM,aAAa,sBAAsB;AA6B/C,IAAa,sBAAb,MAAiC;CAC/B;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,QAAmC;AAC7C,OAAK,MAAM,OAAO;AAClB,OAAK,cAAc,OAAO;AAC1B,OAAK,gBAAgB,OAAO;AAC5B,OAAK,YAAY,OAAO;AACxB,OAAK,4BAA4B,OAAO;AACxC,OAAK,4BAA4B,OAAO;;;;;;;;CAS1C,iCAAiC,KAA8C;AAC7E,MAAI,IAAI,YAAY,MAClB,QAAO;EAET,MAAM,OAAO,IAAI;AACjB,SAAO,uBAAuB;GAC5B,iBAAiB;GACjB,SAAS,YAAY;AACnB,UAAM,KAAK,IAAI,gBAAgB;KAC7B,SAAS,IAAI;KACb,SAAS,IAAI;KACb,SAAS;KACT,MAAM;KACN,UAAU;MACR,WAAW,MAAM;MACjB,UAAU,MAAM;MAChB,gBAAgB,MAAM;MACtB,gBAAgB,MAAM;MACvB;KACF,CAAC;;GAEJ,QAAQ,YAAY;AAClB,UAAM,KAAK,IAAI,gBAAgB;KAC7B,SAAS,IAAI;KACb,SAAS,IAAI;KACb,SAAS;KACT,MAAM;KACN,UAAU;MACR,WAAW,MAAM;MACjB,UAAU,MAAM;MAChB,gBAAgB,MAAM;MACtB,gBAAgB,MAAM;MACvB;KACF,CAAC;;GAEL,CAAC;;;;;;;CAQJ,MAAM,kBAAkB,KAAqB,gBAA+C;AAC1F,MAAI,KAAK,cAAc,0BAA0B,CAC/C;EAGF,MAAM,eAAe,KAAK,0BAA0B,eAAe,WAAW;AAC9E,MAAI,CAAC,cAAc,MAAM,CACvB;AAIF,MAAI,cAAc,cADH,KAAK,WAAW,EAAE,SAAS,WAAW,eAAA,IACd,IAAI,aAAa,MAAM,KAAA,YAAe;AAC3E,OAAI,MAAM,EAAE,YAAY,eAAe,YAAY,EAAE,mCAAmC;AACxF;;EAGF,MAAM,aAAa,MAAM,KAAK,YAAY,kBACxC,eAAe,QACf,cACA,eAAe,QAChB;AACD,MAAI,CAAC,WAAW,KACd;AAGF,QAAM,KAAK,IAAI,gBAAgB;GAC7B,SAAS,eAAe;GACxB,SAAS,eAAe;GACxB,SAAS,WAAW,WAAW;GAC/B,MAAM;GACN,UAAU;IACR,WAAW,IAAI,UAAU;IACzB,UAAU,IAAI,UAAU;IACxB,kBAAkB,eAAe,UAAU;IAC3C,gBAAgB,IAAI,UAAU;IAC9B,gBAAgB,IAAI,UAAU;IAC/B;GACF,CAAC;;;;;;;;CASJ,MAAM,wBAAwB,SAAoD;AAChF,QAAM,KAAK,YAAY,sBAAsB,QAAQ,YAAY,yBAAyB;GACxF,YAAY,QAAQ;GACpB,SAAS,QAAQ;GACjB,QAAQ,QAAQ;GAChB,iBAAiB,QAAQ;GACzB,oBAAoB,QAAQ;GAC5B,SAAS,QAAQ;GACjB,GAAI,QAAQ,gBAAgB,KAAA,IAAY,EAAE,aAAa,QAAQ,aAAa,GAAG,EAAE;GAClF,CAAC;AAEF,QAAM,KAAK,0BAA0B,QAAQ;;;CAI/C,6BACE,IACA,SACA,SAC+D;AAC/D,SAAO,KAAK,YAAY,kBAAkB,IAAI,SAAS,QAAQ;;;CAIjE,0BACE,IACA,SACA,SACA,OACA,SACe;AACf,SAAO,KAAK,YAAY,eAAe,IAAI,SAAS,SAAS,OAAO,QAAQ"}
1
+ {"version":3,"file":"outbound-coordinator.js","names":[],"sources":["../../../../src/agent/messaging/outbound-coordinator.ts"],"sourcesContent":["/**\n * OutboundCoordinator — owns the post-turn outbound pipeline.\n *\n * Concentrates the responsibilities that used to be scattered across\n * `AgentService`:\n * - typing-on / typing-off lifecycle for channels that surface \"is typing…\"\n * - final assistant response delivery (silence guard + hook + bus publish)\n * - cross-cutting `webchat_turn_complete` hook + `/goal` post-turn\n * - thin pass-through for extension `message_sending` / `message_sent` hooks\n *\n * The class is intentionally framework-agnostic: it pulls everything it needs\n * via the constructor config (no direct AgentService reference), so it is\n * unit-testable and lets the parent `AgentService` shrink to a coordinator.\n */\n\nimport type { MessageBus, InboundMessage } from '../../infra/bus/index.js';\nimport type { Config } from '../../config/schema.js';\nimport type { HookHandler } from '../lifecycle/hook-handler.js';\nimport type { SessionContext } from '../session/index.js';\nimport { createTypingController, type TypingController } from '../lifecycle/typing.js';\nimport { DEFAULT_ACK_MAX_CHARS, NO_REPLY, shouldSilence } from '../../heartbeat/tokens.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { StreamManager } from './stream-manager.js';\n\nconst log = createLogger('OutboundCoordinator');\n\nexport interface OutboundCoordinatorConfig {\n bus: MessageBus;\n hookHandler: HookHandler;\n streamManager: StreamManager;\n /** Reads the effective config snapshot (honours runtime overrides). */\n getConfig: () => Config | undefined;\n /** Resolves the last visible assistant text for a session (in-memory + agent fallback). */\n getLastAssistantPlainText: (sessionKey: string) => string;\n /**\n * Run the `/goal` post-turn verdict for a completed user turn. Owned by\n * `PersistentGoalService`; OutboundCoordinator only forwards.\n */\n runPersistentGoalPostTurn: (payload: SessionTurnCompletePayload) => Promise<void>;\n}\n\nexport interface SessionTurnCompletePayload {\n sessionKey: string;\n channel: string;\n chatId: string;\n inboundUserText: string;\n assistantPlainText: string;\n aborted: boolean;\n streamError?: string;\n skipPersistentGoalPostTurn?: boolean;\n outboundMetadata?: Record<string, unknown>;\n}\n\nexport class OutboundCoordinator {\n private readonly bus: MessageBus;\n private readonly hookHandler: HookHandler;\n private readonly streamManager: StreamManager;\n private readonly getConfig: () => Config | undefined;\n private readonly getLastAssistantPlainText: (sessionKey: string) => string;\n private readonly runPersistentGoalPostTurn: OutboundCoordinatorConfig['runPersistentGoalPostTurn'];\n\n constructor(config: OutboundCoordinatorConfig) {\n this.bus = config.bus;\n this.hookHandler = config.hookHandler;\n this.streamManager = config.streamManager;\n this.getConfig = config.getConfig;\n this.getLastAssistantPlainText = config.getLastAssistantPlainText;\n this.runPersistentGoalPostTurn = config.runPersistentGoalPostTurn;\n }\n\n /**\n * Build the typing indicator controller for an inbound message. Returns\n * `null` for the CLI channel (no typing UI). Caller is responsible for\n * `start()` and `stop()` (`stop()` should run AFTER the final outbound so\n * Telegram/Weixin see `typing_off` only once the message is delivered).\n */\n createTypingControllerForInbound(msg: InboundMessage): TypingController | null {\n if (msg.channel === 'cli') {\n return null;\n }\n const meta = msg.metadata as Record<string, unknown> | undefined;\n return createTypingController({\n intervalSeconds: 5,\n onStart: async () => {\n await this.bus.publishOutbound({\n channel: msg.channel,\n chat_id: msg.chat_id,\n content: '',\n type: 'typing_on',\n metadata: {\n accountId: meta?.accountId,\n threadId: meta?.threadId,\n sessionWebhook: meta?.sessionWebhook,\n conversationId: meta?.conversationId,\n },\n });\n },\n onStop: async () => {\n await this.bus.publishOutbound({\n channel: msg.channel,\n chat_id: msg.chat_id,\n content: '',\n type: 'typing_off',\n metadata: {\n accountId: meta?.accountId,\n threadId: meta?.threadId,\n sessionWebhook: meta?.sessionWebhook,\n conversationId: meta?.conversationId,\n },\n });\n },\n });\n }\n\n /**\n * Publish the assistant's visible text as the final bus message. Honours the\n * stream-manager \"channel already streamed the final text\" hint, the heartbeat\n * silence guard, and the extension `message_sending` hook.\n */\n async sendFinalResponse(msg: InboundMessage, sessionContext: SessionContext): Promise<void> {\n if (this.streamManager.consumeSkipFinalOutbound()) {\n return;\n }\n\n const finalContent = this.getLastAssistantPlainText(sessionContext.sessionKey);\n if (!finalContent?.trim()) {\n return;\n }\n\n const ackMax = this.getConfig()?.gateway?.heartbeat?.ackMaxChars ?? DEFAULT_ACK_MAX_CHARS;\n if (shouldSilence(finalContent, ackMax) || finalContent.trim() === NO_REPLY) {\n log.debug({ sessionKey: sessionContext.sessionKey }, 'Silent reply — skipping outbound');\n return;\n }\n\n const hookResult = await this.hookHandler.runMessageSending(\n sessionContext.chatId,\n finalContent,\n sessionContext.channel,\n );\n if (!hookResult.send) {\n return;\n }\n\n await this.bus.publishOutbound({\n channel: sessionContext.channel,\n chat_id: sessionContext.chatId,\n content: hookResult.content || finalContent,\n type: 'message',\n metadata: {\n accountId: msg.metadata?.accountId,\n threadId: msg.metadata?.threadId,\n inboundMessageId: msg.metadata?.messageId,\n transcribedVoice: sessionContext.metadata?.transcribedVoice,\n sessionWebhook: msg.metadata?.sessionWebhook,\n conversationId: msg.metadata?.conversationId,\n },\n });\n }\n\n /**\n * Post-turn hook + `/goal` checklist evaluation. Wired into both the bus-driven\n * inbound finally block and the webchat direct-stream finally. The goal verdict\n * itself lives in `PersistentGoalService`; we just trigger the extension hook\n * here and delegate the `/goal` work.\n */\n async emitSessionTurnComplete(payload: SessionTurnCompletePayload): Promise<void> {\n await this.hookHandler.triggerWithSessionKey(payload.sessionKey, 'webchat_turn_complete', {\n sessionKey: payload.sessionKey,\n channel: payload.channel,\n chatId: payload.chatId,\n inboundUserText: payload.inboundUserText,\n assistantPlainText: payload.assistantPlainText,\n aborted: payload.aborted,\n ...(payload.streamError !== undefined ? { streamError: payload.streamError } : {}),\n });\n\n await this.runPersistentGoalPostTurn(payload);\n }\n\n /** Extension hook pass-through (Gateway ChannelManager). */\n invokeOutboundMessageSending(\n to: string,\n content: string,\n channel: string,\n ): Promise<{ send: boolean; content?: string; reason?: string }> {\n return this.hookHandler.runMessageSending(to, content, channel);\n }\n\n /** Extension hook pass-through (Gateway ChannelManager). */\n invokeOutboundMessageSent(\n to: string,\n content: string,\n success: boolean,\n error: string | undefined,\n channel: string,\n ): Promise<void> {\n return this.hookHandler.runMessageSent(to, content, success, error, channel);\n }\n}\n"],"mappings":";;;;;aAqBqD;AAGrD,MAAM,MAAM,aAAa,sBAAsB;AA6B/C,IAAa,sBAAb,MAAiC;CAC/B;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,QAAmC;AAC7C,OAAK,MAAM,OAAO;AAClB,OAAK,cAAc,OAAO;AAC1B,OAAK,gBAAgB,OAAO;AAC5B,OAAK,YAAY,OAAO;AACxB,OAAK,4BAA4B,OAAO;AACxC,OAAK,4BAA4B,OAAO;;;;;;;;CAS1C,iCAAiC,KAA8C;AAC7E,MAAI,IAAI,YAAY,MAClB,QAAO;EAET,MAAM,OAAO,IAAI;AACjB,SAAO,uBAAuB;GAC5B,iBAAiB;GACjB,SAAS,YAAY;AACnB,UAAM,KAAK,IAAI,gBAAgB;KAC7B,SAAS,IAAI;KACb,SAAS,IAAI;KACb,SAAS;KACT,MAAM;KACN,UAAU;MACR,WAAW,MAAM;MACjB,UAAU,MAAM;MAChB,gBAAgB,MAAM;MACtB,gBAAgB,MAAM;MACvB;KACF,CAAC;;GAEJ,QAAQ,YAAY;AAClB,UAAM,KAAK,IAAI,gBAAgB;KAC7B,SAAS,IAAI;KACb,SAAS,IAAI;KACb,SAAS;KACT,MAAM;KACN,UAAU;MACR,WAAW,MAAM;MACjB,UAAU,MAAM;MAChB,gBAAgB,MAAM;MACtB,gBAAgB,MAAM;MACvB;KACF,CAAC;;GAEL,CAAC;;;;;;;CAQJ,MAAM,kBAAkB,KAAqB,gBAA+C;AAC1F,MAAI,KAAK,cAAc,0BAA0B,CAC/C;EAGF,MAAM,eAAe,KAAK,0BAA0B,eAAe,WAAW;AAC9E,MAAI,CAAC,cAAc,MAAM,CACvB;AAIF,MAAI,cAAc,cADH,KAAK,WAAW,EAAE,SAAS,WAAW,eAAA,IACd,IAAI,aAAa,MAAM,KAAA,YAAe;AAC3E,OAAI,MAAM,EAAE,YAAY,eAAe,YAAY,EAAE,mCAAmC;AACxF;;EAGF,MAAM,aAAa,MAAM,KAAK,YAAY,kBACxC,eAAe,QACf,cACA,eAAe,QAChB;AACD,MAAI,CAAC,WAAW,KACd;AAGF,QAAM,KAAK,IAAI,gBAAgB;GAC7B,SAAS,eAAe;GACxB,SAAS,eAAe;GACxB,SAAS,WAAW,WAAW;GAC/B,MAAM;GACN,UAAU;IACR,WAAW,IAAI,UAAU;IACzB,UAAU,IAAI,UAAU;IACxB,kBAAkB,IAAI,UAAU;IAChC,kBAAkB,eAAe,UAAU;IAC3C,gBAAgB,IAAI,UAAU;IAC9B,gBAAgB,IAAI,UAAU;IAC/B;GACF,CAAC;;;;;;;;CASJ,MAAM,wBAAwB,SAAoD;AAChF,QAAM,KAAK,YAAY,sBAAsB,QAAQ,YAAY,yBAAyB;GACxF,YAAY,QAAQ;GACpB,SAAS,QAAQ;GACjB,QAAQ,QAAQ;GAChB,iBAAiB,QAAQ;GACzB,oBAAoB,QAAQ;GAC5B,SAAS,QAAQ;GACjB,GAAI,QAAQ,gBAAgB,KAAA,IAAY,EAAE,aAAa,QAAQ,aAAa,GAAG,EAAE;GAClF,CAAC;AAEF,QAAM,KAAK,0BAA0B,QAAQ;;;CAI/C,6BACE,IACA,SACA,SAC+D;AAC/D,SAAO,KAAK,YAAY,kBAAkB,IAAI,SAAS,QAAQ;;;CAIjE,0BACE,IACA,SACA,SACA,OACA,SACe;AACf,SAAO,KAAK,YAAY,eAAe,IAAI,SAAS,SAAS,OAAO,QAAQ"}
@@ -7,6 +7,7 @@
7
7
  import type { ProgressStage } from '../lifecycle/progress.js';
8
8
  export interface StreamHandle {
9
9
  update: (text: string) => void;
10
+ updateReasoning?: (text: string) => void;
10
11
  updateProgress?: (text: string, stage: ProgressStage, detail?: string) => void;
11
12
  setProgress?: (stage: ProgressStage, detail?: string) => void;
12
13
  end: () => Promise<void>;
@@ -37,6 +38,7 @@ export declare class StreamManager {
37
38
  * Update the stream with text content
38
39
  */
39
40
  update(text: string): void;
41
+ updateReasoning(text: string): void;
40
42
  /**
41
43
  * End the stream and clear the handle
42
44
  */
@@ -32,6 +32,9 @@ var StreamManager = class {
32
32
  update(text) {
33
33
  this.currentHandle?.update(text);
34
34
  }
35
+ updateReasoning(text) {
36
+ this.currentHandle?.updateReasoning?.(text);
37
+ }
35
38
  /**
36
39
  * End the stream and clear the handle
37
40
  */
@@ -1 +1 @@
1
- {"version":3,"file":"stream-manager.js","names":[],"sources":["../../../../src/agent/messaging/stream-manager.ts"],"sourcesContent":["/**\n * Stream Manager - Manages stream handles for progress updates\n *\n * Handles creation and lifecycle of stream handles for sending progress updates\n * and streaming responses.\n */\n\nimport type { ProgressStage } from '../lifecycle/progress.js';\n\nexport interface StreamHandle {\n update: (text: string) => void;\n updateProgress?: (text: string, stage: ProgressStage, detail?: string) => void;\n setProgress?: (stage: ProgressStage, detail?: string) => void;\n end: () => Promise<void>;\n abort: () => Promise<void>;\n messageId: () => number | undefined;\n skipFinalOutbound?: () => boolean;\n}\n\nexport class StreamManager {\n private currentHandle: StreamHandle | null = null;\n private skipFinalOutboundPending = false;\n\n /**\n * Set the current stream handle\n */\n setHandle(handle: StreamHandle): void {\n this.currentHandle = handle;\n }\n\n /**\n * Get the current stream handle\n */\n getHandle(): StreamHandle | null {\n return this.currentHandle;\n }\n\n /**\n * Clear the current stream handle\n */\n clearHandle(): void {\n this.currentHandle = null;\n }\n\n /**\n * Check if a stream handle is currently active\n */\n hasActiveHandle(): boolean {\n return this.currentHandle !== null;\n }\n\n /**\n * Update the stream with text content\n */\n update(text: string): void {\n this.currentHandle?.update(text);\n }\n\n /**\n * End the stream and clear the handle\n */\n async end(): Promise<void> {\n if (this.currentHandle) {\n const handle = this.currentHandle;\n await handle.end();\n this.skipFinalOutboundPending = handle.skipFinalOutbound?.() ?? false;\n this.clearHandle();\n } else {\n this.skipFinalOutboundPending = false;\n }\n }\n\n /**\n * Abort the stream and clear the handle\n */\n async abort(): Promise<void> {\n if (this.currentHandle) {\n await this.currentHandle.abort();\n this.skipFinalOutboundPending = false;\n this.clearHandle();\n }\n }\n\n /**\n * Whether the channel already delivered the final assistant text (e.g. Telegram streaming).\n * Consumes the flag for one outbound decision.\n */\n consumeSkipFinalOutbound(): boolean {\n const v = this.skipFinalOutboundPending;\n this.skipFinalOutboundPending = false;\n return v;\n }\n\n /**\n * Get the current message ID from the stream handle\n */\n getMessageId(): number | undefined {\n return this.currentHandle?.messageId();\n }\n\n /**\n * Update progress via the stream handle\n */\n updateProgress(text: string, stage: ProgressStage, detail?: string): void {\n if (this.currentHandle?.updateProgress) {\n this.currentHandle.updateProgress(text, stage, detail);\n }\n }\n\n /**\n * Set progress stage via the stream handle\n */\n setProgress(stage: ProgressStage, detail?: string): void {\n if (this.currentHandle?.setProgress) {\n this.currentHandle.setProgress(stage, detail);\n }\n }\n}\n"],"mappings":";AAmBA,IAAa,gBAAb,MAA2B;CACzB,gBAA6C;CAC7C,2BAAmC;;;;CAKnC,UAAU,QAA4B;AACpC,OAAK,gBAAgB;;;;;CAMvB,YAAiC;AAC/B,SAAO,KAAK;;;;;CAMd,cAAoB;AAClB,OAAK,gBAAgB;;;;;CAMvB,kBAA2B;AACzB,SAAO,KAAK,kBAAkB;;;;;CAMhC,OAAO,MAAoB;AACzB,OAAK,eAAe,OAAO,KAAK;;;;;CAMlC,MAAM,MAAqB;AACzB,MAAI,KAAK,eAAe;GACtB,MAAM,SAAS,KAAK;AACpB,SAAM,OAAO,KAAK;AAClB,QAAK,2BAA2B,OAAO,qBAAqB,IAAI;AAChE,QAAK,aAAa;QAElB,MAAK,2BAA2B;;;;;CAOpC,MAAM,QAAuB;AAC3B,MAAI,KAAK,eAAe;AACtB,SAAM,KAAK,cAAc,OAAO;AAChC,QAAK,2BAA2B;AAChC,QAAK,aAAa;;;;;;;CAQtB,2BAAoC;EAClC,MAAM,IAAI,KAAK;AACf,OAAK,2BAA2B;AAChC,SAAO;;;;;CAMT,eAAmC;AACjC,SAAO,KAAK,eAAe,WAAW;;;;;CAMxC,eAAe,MAAc,OAAsB,QAAuB;AACxE,MAAI,KAAK,eAAe,eACtB,MAAK,cAAc,eAAe,MAAM,OAAO,OAAO;;;;;CAO1D,YAAY,OAAsB,QAAuB;AACvD,MAAI,KAAK,eAAe,YACtB,MAAK,cAAc,YAAY,OAAO,OAAO"}
1
+ {"version":3,"file":"stream-manager.js","names":[],"sources":["../../../../src/agent/messaging/stream-manager.ts"],"sourcesContent":["/**\n * Stream Manager - Manages stream handles for progress updates\n *\n * Handles creation and lifecycle of stream handles for sending progress updates\n * and streaming responses.\n */\n\nimport type { ProgressStage } from '../lifecycle/progress.js';\n\nexport interface StreamHandle {\n update: (text: string) => void;\n updateReasoning?: (text: string) => void;\n updateProgress?: (text: string, stage: ProgressStage, detail?: string) => void;\n setProgress?: (stage: ProgressStage, detail?: string) => void;\n end: () => Promise<void>;\n abort: () => Promise<void>;\n messageId: () => number | undefined;\n skipFinalOutbound?: () => boolean;\n}\n\nexport class StreamManager {\n private currentHandle: StreamHandle | null = null;\n private skipFinalOutboundPending = false;\n\n /**\n * Set the current stream handle\n */\n setHandle(handle: StreamHandle): void {\n this.currentHandle = handle;\n }\n\n /**\n * Get the current stream handle\n */\n getHandle(): StreamHandle | null {\n return this.currentHandle;\n }\n\n /**\n * Clear the current stream handle\n */\n clearHandle(): void {\n this.currentHandle = null;\n }\n\n /**\n * Check if a stream handle is currently active\n */\n hasActiveHandle(): boolean {\n return this.currentHandle !== null;\n }\n\n /**\n * Update the stream with text content\n */\n update(text: string): void {\n this.currentHandle?.update(text);\n }\n\n updateReasoning(text: string): void {\n this.currentHandle?.updateReasoning?.(text);\n }\n\n /**\n * End the stream and clear the handle\n */\n async end(): Promise<void> {\n if (this.currentHandle) {\n const handle = this.currentHandle;\n await handle.end();\n this.skipFinalOutboundPending = handle.skipFinalOutbound?.() ?? false;\n this.clearHandle();\n } else {\n this.skipFinalOutboundPending = false;\n }\n }\n\n /**\n * Abort the stream and clear the handle\n */\n async abort(): Promise<void> {\n if (this.currentHandle) {\n await this.currentHandle.abort();\n this.skipFinalOutboundPending = false;\n this.clearHandle();\n }\n }\n\n /**\n * Whether the channel already delivered the final assistant text (e.g. Telegram streaming).\n * Consumes the flag for one outbound decision.\n */\n consumeSkipFinalOutbound(): boolean {\n const v = this.skipFinalOutboundPending;\n this.skipFinalOutboundPending = false;\n return v;\n }\n\n /**\n * Get the current message ID from the stream handle\n */\n getMessageId(): number | undefined {\n return this.currentHandle?.messageId();\n }\n\n /**\n * Update progress via the stream handle\n */\n updateProgress(text: string, stage: ProgressStage, detail?: string): void {\n if (this.currentHandle?.updateProgress) {\n this.currentHandle.updateProgress(text, stage, detail);\n }\n }\n\n /**\n * Set progress stage via the stream handle\n */\n setProgress(stage: ProgressStage, detail?: string): void {\n if (this.currentHandle?.setProgress) {\n this.currentHandle.setProgress(stage, detail);\n }\n }\n}\n"],"mappings":";AAoBA,IAAa,gBAAb,MAA2B;CACzB,gBAA6C;CAC7C,2BAAmC;;;;CAKnC,UAAU,QAA4B;AACpC,OAAK,gBAAgB;;;;;CAMvB,YAAiC;AAC/B,SAAO,KAAK;;;;;CAMd,cAAoB;AAClB,OAAK,gBAAgB;;;;;CAMvB,kBAA2B;AACzB,SAAO,KAAK,kBAAkB;;;;;CAMhC,OAAO,MAAoB;AACzB,OAAK,eAAe,OAAO,KAAK;;CAGlC,gBAAgB,MAAoB;AAClC,OAAK,eAAe,kBAAkB,KAAK;;;;;CAM7C,MAAM,MAAqB;AACzB,MAAI,KAAK,eAAe;GACtB,MAAM,SAAS,KAAK;AACpB,SAAM,OAAO,KAAK;AAClB,QAAK,2BAA2B,OAAO,qBAAqB,IAAI;AAChE,QAAK,aAAa;QAElB,MAAK,2BAA2B;;;;;CAOpC,MAAM,QAAuB;AAC3B,MAAI,KAAK,eAAe;AACtB,SAAM,KAAK,cAAc,OAAO;AAChC,QAAK,2BAA2B;AAChC,QAAK,aAAa;;;;;;;CAQtB,2BAAoC;EAClC,MAAM,IAAI,KAAK;AACf,OAAK,2BAA2B;AAChC,SAAO;;;;;CAMT,eAAmC;AACjC,SAAO,KAAK,eAAe,WAAW;;;;;CAMxC,eAAe,MAAc,OAAsB,QAAuB;AACxE,MAAI,KAAK,eAAe,eACtB,MAAK,cAAc,eAAe,MAAM,OAAO,OAAO;;;;;CAO1D,YAAY,OAAsB,QAAuB;AACvD,MAAI,KAAK,eAAe,YACtB,MAAK,cAAc,YAAY,OAAO,OAAO"}
@@ -64,10 +64,6 @@ export declare class AgentOrchestrator {
64
64
  * Process a message through the agent orchestration pipeline
65
65
  */
66
66
  process(msg: InboundMessage, context: SessionContext): Promise<void>;
67
- /**
68
- * Build an agent message from an inbound message
69
- */
70
- private buildUserMessage;
71
67
  /**
72
68
  * Get the current agent model ID
73
69
  */
@@ -2,13 +2,12 @@ import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
3
  import "../memory/dreaming/constants.js";
4
4
  import { resolveDreamingConfig } from "../memory/dreaming/config.js";
5
+ import { persistInboundAttachments } from "../../channels/attachments/inbound-persist.js";
6
+ import { buildTranscriptUserMessage, clearPendingTranscriptUserMessage, hydrateUserTurnForLlm, setPendingTranscriptUserMessage } from "../inbound/attachment-pipeline.js";
5
7
  import { extractAgentUserPlainText } from "../memory/user-message-text.js";
6
- import { formatInboundFileTextBlock, persistInboundAttachmentsToWorkspace } from "../../channels/attachments/inbound-persist.js";
7
8
  import { resolveEffectiveThinkingLevel } from "../../session/thinking-resolve.js";
8
9
  import { abortEmbeddedRun } from "../embedded/runs.js";
9
10
  import { runEmbeddedTurnForSession } from "../embedded/run-for-session.js";
10
- import { expandAtFileMentionsInPlainText } from "../context/expand-at-file-mentions.js";
11
- import { resolveInboundImageContentParts } from "../image/inbound-image-handling.js";
12
11
  import { runDreamingDeepPromotion } from "../memory/dreaming/deep-promotion.js";
13
12
  import { appendDreamingEvent } from "../memory/dreaming/events.js";
14
13
  import { runLightSweep } from "../memory/dreaming/light-sweep.js";
@@ -113,28 +112,50 @@ var AgentOrchestrator = class {
113
112
  }
114
113
  try {
115
114
  await this.sessionHydrator.model(sessionKey);
115
+ const channelSystemPrompt = typeof context.metadata?.channelSystemPrompt === "string" ? context.metadata.channelSystemPrompt.trim() : "";
116
+ if (channelSystemPrompt) {
117
+ this.agentManager.getOrCreateAgent(sessionKey);
118
+ this.agentManager.applyTurnChannelSystemPrompt(sessionKey, channelSystemPrompt);
119
+ }
116
120
  const thinkingDefault = this.getThinkingDefaultForSession?.(sessionKey) ?? this.getThinkingDefault();
117
121
  const thinkingLevel = await resolveEffectiveThinkingLevel(this.sessionConfigStore, sessionKey, null, thinkingDefault);
118
122
  this.agentManager.setThinkingLevel(sessionKey, thinkingLevel);
119
- const persistedAttachments = await persistInboundAttachmentsToWorkspace(this.getAgentInternalStorageRootForSession(sessionKey), sessionKey, msg.attachments);
120
- const userMessage = await this.buildUserMessage({
121
- ...msg,
122
- attachments: persistedAttachments ?? msg.attachments
123
- }, sessionKey);
123
+ const persistedAttachments = await persistInboundAttachments(msg.attachments);
124
+ const modelRef = this.modelManager.getModelForSession(sessionKey);
125
+ const userMessage = await buildTranscriptUserMessage({
126
+ text: msg.content,
127
+ prepared: persistedAttachments,
128
+ sessionKey,
129
+ modelRef,
130
+ config: this.getConfig?.(),
131
+ agentManager: this.agentManager
132
+ });
133
+ setPendingTranscriptUserMessage(sessionKey, userMessage);
124
134
  const userPlainForMemory = extractAgentUserPlainText(userMessage);
125
135
  const userMessageForModel = await this.agentManager.applyMemoryPrefetchToUserMessage(userMessage, sessionKey);
126
- this.feedbackCoordinator.startTask();
127
- const turnResult = await runEmbeddedTurnForSession({
128
- sessionKey,
129
- userMessage: userMessageForModel,
130
- sessionStore: this.sessionStore,
131
- agentManager: this.agentManager,
132
- modelManager: this.modelManager,
133
- thinkingOverride: thinkingLevel,
134
- getConfig: this.getConfig,
135
- beforeTurn: () => this.agentManager.beginBackgroundReviewUserTurn(sessionKey),
136
- onEvent: (event) => this.onEmbeddedStreamEvent?.(sessionKey, event)
136
+ const llmTurn = await hydrateUserTurnForLlm({
137
+ message: userMessage,
138
+ modelRef
137
139
  });
140
+ this.feedbackCoordinator.startTask();
141
+ const turnResult = await (async () => {
142
+ try {
143
+ return await runEmbeddedTurnForSession({
144
+ sessionKey,
145
+ userMessage: userMessageForModel,
146
+ llmImages: llmTurn.images,
147
+ sessionStore: this.sessionStore,
148
+ agentManager: this.agentManager,
149
+ modelManager: this.modelManager,
150
+ thinkingOverride: thinkingLevel,
151
+ getConfig: this.getConfig,
152
+ beforeTurn: () => this.agentManager.beginBackgroundReviewUserTurn(sessionKey),
153
+ onEvent: (event) => this.onEmbeddedStreamEvent?.(sessionKey, event)
154
+ });
155
+ } finally {
156
+ clearPendingTranscriptUserMessage(sessionKey, userMessage);
157
+ }
158
+ })();
138
159
  this.agentManager.afterAgentTurn(sessionKey, userPlainForMemory);
139
160
  this.agentManager.scheduleBackgroundReviewAfterUserTurn(sessionKey);
140
161
  if (turnResult.ok) {
@@ -155,94 +176,6 @@ var AgentOrchestrator = class {
155
176
  }
156
177
  }
157
178
  /**
158
- * Build an agent message from an inbound message
159
- */
160
- async buildUserMessage(msg, sessionKey) {
161
- const storageRootAbs = this.getAgentInternalStorageRootForSession(sessionKey);
162
- let textBody = msg.content.trimStart().startsWith("/skill:") ? this.agentManager.expandSkillUserText(msg.content) : msg.content;
163
- if (/@file:/.test(textBody)) {
164
- const root = this.agentManager.getResolvedWorkspaceForSession(sessionKey);
165
- textBody = await expandAtFileMentionsInPlainText(textBody, root);
166
- }
167
- if (!msg.attachments || msg.attachments.length === 0) return {
168
- role: "user",
169
- content: textBody,
170
- timestamp: Date.now()
171
- };
172
- const modelRef = this.modelManager.getModelForSession(sessionKey);
173
- const cfg = this.getConfig?.();
174
- const messageContent = [];
175
- if (msg.content.trim()) messageContent.push({
176
- type: "text",
177
- text: textBody
178
- });
179
- const attachments = msg.attachments;
180
- let i = 0;
181
- while (i < attachments.length) {
182
- const att = attachments[i];
183
- if (att.type === "image" || att.type === "photo" || Boolean(att.mimeType?.startsWith("image/"))) {
184
- const group = [];
185
- while (i < attachments.length) {
186
- const a = attachments[i];
187
- if (!(a.type === "image" || a.type === "photo" || Boolean(a.mimeType?.startsWith("image/")))) break;
188
- if (!a.data || a.data.length === 0) {
189
- log.warn({
190
- type: a.type,
191
- name: a.name
192
- }, "Empty image data, skipping");
193
- i += 1;
194
- continue;
195
- }
196
- group.push({
197
- data: a.data,
198
- mimeType: a.mimeType || "image/jpeg"
199
- });
200
- i += 1;
201
- }
202
- if (group.length > 0) {
203
- const parts = await resolveInboundImageContentParts({
204
- modelRef,
205
- cfg,
206
- userTextForContext: msg.content.trim() ? textBody : "",
207
- images: group
208
- });
209
- messageContent.push(...parts);
210
- }
211
- } else {
212
- const fileBlock = formatInboundFileTextBlock({
213
- type: att.type,
214
- mimeType: att.mimeType,
215
- name: att.name,
216
- size: att.size,
217
- workspaceRelativePath: att.workspaceRelativePath
218
- }, storageRootAbs);
219
- messageContent.push({
220
- type: "text",
221
- text: fileBlock
222
- });
223
- i += 1;
224
- }
225
- }
226
- const hasText = messageContent.some((item) => item.type === "text");
227
- if (messageContent.some((item) => item.type === "image") && !hasText) messageContent.unshift({
228
- type: "text",
229
- text: "Please analyze the image(s) I sent."
230
- });
231
- if (messageContent.length === 0) {
232
- log.warn({ attachmentCount: msg.attachments.length }, "All attachments were skipped, falling back to text message");
233
- return {
234
- role: "user",
235
- content: textBody || "[Image attachment could not be processed]",
236
- timestamp: Date.now()
237
- };
238
- }
239
- return {
240
- role: "user",
241
- content: messageContent,
242
- timestamp: Date.now()
243
- };
244
- }
245
- /**
246
179
  * Get the current agent model ID
247
180
  */
248
181
  getCurrentModel() {
@@ -1 +1 @@
1
- {"version":3,"file":"agent-orchestrator.js","names":[],"sources":["../../../../src/agent/orchestration/agent-orchestrator.ts"],"sourcesContent":["/**\n * Agent Orchestrator - Coordinates Agent execution flow\n *\n * Manages the complete agent execution pipeline from message processing\n * to response generation.\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { Config } from '../../config/schema.js';\nimport type { InboundMessage } from '../../infra/bus/index.js';\nimport type { SessionConfigStore, SessionStore } from '../../session/index.js';\nimport type { SessionHydrator } from '../session/index.js';\nimport { resolveEffectiveThinkingLevel } from '../../session/thinking-resolve.js';\nimport type { ThinkLevel } from '../transcript/thinking-types.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { SessionContext } from '../session/session-context.js';\nimport type { AgentEventHandler } from './agent-event-handler.js';\nimport type { FeedbackCoordinator } from '../feedback/feedback-coordinator.js';\nimport type { AgentManager } from '../agent-manager.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { extractAgentUserPlainText } from '../memory/user-message-text.js';\nimport { abortEmbeddedRun } from '../embedded/runs.js';\nimport { runEmbeddedTurnForSession } from '../embedded/run-for-session.js';\nimport type { EmbeddedStreamEvent } from '../embedded/types.js';\nimport {\n persistInboundAttachmentsToWorkspace,\n formatInboundFileTextBlock,\n} from '../../channels/attachments/inbound-persist.js';\nimport { expandAtFileMentionsInPlainText } from '../context/expand-at-file-mentions.js';\nimport { resolveInboundImageContentParts } from '../image/inbound-image-handling.js';\nimport {\n DREAMING_SWEEP_TOKEN,\n DREAMING_LIGHT_SWEEP_TOKEN,\n DREAMING_REM_SWEEP_TOKEN,\n} from '../memory/dreaming/constants.js';\nimport { resolveDreamingConfig } from '../memory/dreaming/config.js';\nimport { runDreamingDeepPromotion } from '../memory/dreaming/deep-promotion.js';\nimport { appendDreamingEvent, type DreamingEvent } from '../memory/dreaming/events.js';\nimport { runLightSweep } from '../memory/dreaming/light-sweep.js';\nimport { runRemPatterns } from '../memory/dreaming/rem-patterns.js';\n\nconst log = createLogger('AgentOrchestrator');\n\nexport interface AgentOrchestratorConfig {\n agentManager: AgentManager;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n eventHandler: AgentEventHandler;\n feedbackCoordinator: FeedbackCoordinator;\n sessionConfigStore: SessionConfigStore;\n /** Per-session hydration (workspace override + model override) before the agent runs. */\n sessionHydrator: SessionHydrator;\n getThinkingDefault: () => ThinkLevel | undefined;\n /** Per-session default from merged `agents.list` / defaults (optional). */\n getThinkingDefaultForSession?: (sessionKey: string) => ThinkLevel | undefined;\n /** Default workspace root when no per-session resolver is set. */\n workspaceRoot: string;\n /** Per-agent workspace root for attachments (optional; defaults to `workspaceRoot`). */\n getWorkspaceRootForSession?: (sessionKey: string) => string;\n /** Agent home (`…/agents/<id>/`) for inbound/TTS files — keeps internal state out of the markdown workspace. */\n getAgentInternalStorageRootForSession?: (sessionKey: string) => string;\n /** Fire-and-forget after full session persist (e.g. LLM session title); not called from mid-turn snapshots. */\n enqueueAutoTitle?: (sessionKey: string) => void;\n /** For per-turn timeout via `agents.defaults.maxTaskDurationMs`. */\n getConfig?: () => Config | undefined;\n /** Channel streaming: token/tool events from pi embedded session. */\n onEmbeddedStreamEvent?: (sessionKey: string, event: EmbeddedStreamEvent) => void;\n /** Called after a successful embedded turn with assistant plain text. */\n onEmbeddedTurnComplete?: (sessionKey: string, lastAssistantText?: string) => void;\n}\n\nexport class AgentOrchestrator {\n private agentManager: AgentManager;\n private sessionStore: SessionStore;\n private modelManager: ModelManager;\n private eventHandler: AgentEventHandler;\n private feedbackCoordinator: FeedbackCoordinator;\n private sessionConfigStore: SessionConfigStore;\n private sessionHydrator: SessionHydrator;\n private getThinkingDefault: () => ThinkLevel | undefined;\n private getThinkingDefaultForSession?: (sessionKey: string) => ThinkLevel | undefined;\n private workspaceRoot: string;\n private getWorkspaceRootForSession?: (sessionKey: string) => string;\n private getAgentInternalStorageRootForSession: (sessionKey: string) => string;\n private enqueueAutoTitle?: (sessionKey: string) => void;\n private getConfig?: () => Config | undefined;\n private onEmbeddedStreamEvent?: (sessionKey: string, event: EmbeddedStreamEvent) => void;\n private onEmbeddedTurnComplete?: (sessionKey: string, lastAssistantText?: string) => void;\n\n constructor(config: AgentOrchestratorConfig) {\n this.agentManager = config.agentManager;\n this.sessionStore = config.sessionStore;\n this.modelManager = config.modelManager;\n this.eventHandler = config.eventHandler;\n this.feedbackCoordinator = config.feedbackCoordinator;\n this.sessionConfigStore = config.sessionConfigStore;\n this.sessionHydrator = config.sessionHydrator;\n this.getThinkingDefault = config.getThinkingDefault;\n this.getThinkingDefaultForSession = config.getThinkingDefaultForSession;\n this.workspaceRoot = config.workspaceRoot;\n this.getWorkspaceRootForSession = config.getWorkspaceRootForSession;\n this.getAgentInternalStorageRootForSession =\n config.getAgentInternalStorageRootForSession ??\n ((sk) => this.getWorkspaceRootForSession?.(sk) ?? this.workspaceRoot);\n this.enqueueAutoTitle = config.enqueueAutoTitle;\n this.getConfig = config.getConfig;\n this.onEmbeddedStreamEvent = config.onEmbeddedStreamEvent;\n this.onEmbeddedTurnComplete = config.onEmbeddedTurnComplete;\n }\n\n /**\n * Process a message through the agent orchestration pipeline\n */\n async process(msg: InboundMessage, context: SessionContext): Promise<void> {\n const { sessionKey } = context;\n\n log.debug({ sessionKey }, 'Processing message through agent orchestrator');\n\n await this.sessionHydrator.workspace(sessionKey);\n\n // Dreaming: short-circuit cron-triggered sweep tokens into maintenance runs.\n // This avoids spending LLM tokens for scheduled memory consolidation.\n if (\n typeof msg.content === 'string' &&\n (sessionKey.startsWith('cron:') || context.channel === 'cron')\n ) {\n const content = msg.content;\n const isDreamingSweep =\n content.includes(DREAMING_SWEEP_TOKEN) ||\n content.includes(DREAMING_LIGHT_SWEEP_TOKEN) ||\n content.includes(DREAMING_REM_SWEEP_TOKEN);\n\n if (isDreamingSweep) {\n const workspaceDir = this.agentManager.getResolvedWorkspaceForSession(sessionKey);\n const resolved = resolveDreamingConfig(this.getConfig?.());\n const t0 = Date.now();\n\n if (content.includes(DREAMING_LIGHT_SWEEP_TOKEN)) {\n const result = await runLightSweep({ workspaceDir, config: resolved.phases.light });\n const event: DreamingEvent = {\n timestamp: new Date().toISOString(), phase: 'light',\n ok: result.ok, reason: result.reason, durationMs: Date.now() - t0,\n scannedEntries: result.scannedEntries, newSignals: result.newSignals, deduped: result.deduped,\n };\n await appendDreamingEvent(workspaceDir, event);\n } else if (content.includes(DREAMING_REM_SWEEP_TOKEN)) {\n const result = await runRemPatterns({ workspaceDir, config: resolved.phases.rem });\n const event: DreamingEvent = {\n timestamp: new Date().toISOString(), phase: 'rem',\n ok: result.ok, reason: result.reason, durationMs: Date.now() - t0,\n patternsDiscovered: result.patternsDiscovered, entriesAnalyzed: result.entriesAnalyzed,\n };\n await appendDreamingEvent(workspaceDir, event);\n } else {\n const result = await runDreamingDeepPromotion({ workspaceDir, config: resolved.phases.deep });\n const event: DreamingEvent = {\n timestamp: new Date().toISOString(), phase: 'deep',\n ok: result.ok, reason: result.reason, durationMs: Date.now() - t0,\n candidates: result.candidates, applied: result.applied,\n };\n await appendDreamingEvent(workspaceDir, event);\n }\n return;\n }\n }\n\n try {\n await this.sessionHydrator.model(sessionKey);\n\n const thinkingDefault =\n this.getThinkingDefaultForSession?.(sessionKey) ?? this.getThinkingDefault();\n const thinkingLevel = await resolveEffectiveThinkingLevel(\n this.sessionConfigStore,\n sessionKey,\n null,\n thinkingDefault,\n );\n this.agentManager.setThinkingLevel(sessionKey, thinkingLevel);\n\n // Persist inbound files (Telegram, etc.) under agent home, then build user message\n const storageRoot = this.getAgentInternalStorageRootForSession(sessionKey);\n const persistedAttachments = await persistInboundAttachmentsToWorkspace(\n storageRoot,\n sessionKey,\n msg.attachments,\n );\n const userMessage = await this.buildUserMessage(\n {\n ...msg,\n attachments: persistedAttachments ?? msg.attachments,\n },\n sessionKey,\n );\n const userPlainForMemory = extractAgentUserPlainText(userMessage);\n const userMessageForModel = await this.agentManager.applyMemoryPrefetchToUserMessage(\n userMessage,\n sessionKey,\n );\n\n this.feedbackCoordinator.startTask();\n\n const turnResult = await runEmbeddedTurnForSession({\n sessionKey,\n userMessage: userMessageForModel,\n sessionStore: this.sessionStore,\n agentManager: this.agentManager,\n modelManager: this.modelManager,\n thinkingOverride: thinkingLevel,\n getConfig: this.getConfig,\n beforeTurn: () => this.agentManager.beginBackgroundReviewUserTurn(sessionKey),\n onEvent: (event) => this.onEmbeddedStreamEvent?.(sessionKey, event),\n });\n\n this.agentManager.afterAgentTurn(sessionKey, userPlainForMemory);\n this.agentManager.scheduleBackgroundReviewAfterUserTurn(sessionKey);\n\n if (turnResult.ok) {\n this.onEmbeddedTurnComplete?.(sessionKey, turnResult.lastAssistantText);\n this.enqueueAutoTitle?.(sessionKey);\n } else if (turnResult.errorMessage) {\n log.warn({ sessionKey, errorMessage: turnResult.errorMessage }, 'Embedded inbound turn failed');\n }\n\n this.feedbackCoordinator.endTask();\n\n } catch (error) {\n log.error({ err: error, sessionKey }, 'Error in agent orchestration');\n this.feedbackCoordinator.endTask();\n throw error;\n }\n }\n\n /**\n * Build an agent message from an inbound message\n */\n private async buildUserMessage(msg: InboundMessage, sessionKey: string): Promise<AgentMessage> {\n const storageRootAbs = this.getAgentInternalStorageRootForSession(sessionKey);\n let textBody = msg.content.trimStart().startsWith('/skill:')\n ? this.agentManager.expandSkillUserText(msg.content)\n : msg.content;\n\n if (/@file:/.test(textBody)) {\n const root = this.agentManager.getResolvedWorkspaceForSession(sessionKey);\n textBody = await expandAtFileMentionsInPlainText(textBody, root);\n }\n\n if (!msg.attachments || msg.attachments.length === 0) {\n return {\n role: 'user',\n content: textBody,\n timestamp: Date.now(),\n };\n }\n\n const modelRef = this.modelManager.getModelForSession(sessionKey);\n const cfg = this.getConfig?.();\n\n const messageContent: Array<\n { type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }\n > = [];\n\n if (msg.content.trim()) {\n messageContent.push({ type: 'text', text: textBody });\n }\n\n const attachments = msg.attachments;\n let i = 0;\n while (i < attachments.length) {\n const att = attachments[i]!;\n const isImage =\n att.type === 'image' || att.type === 'photo' || Boolean(att.mimeType?.startsWith('image/'));\n\n if (isImage) {\n const group: Array<{ data: string; mimeType: string }> = [];\n while (i < attachments.length) {\n const a = attachments[i]!;\n const img =\n a.type === 'image' || a.type === 'photo' || Boolean(a.mimeType?.startsWith('image/'));\n if (!img) {\n break;\n }\n if (!a.data || a.data.length === 0) {\n log.warn({ type: a.type, name: a.name }, 'Empty image data, skipping');\n i += 1;\n continue;\n }\n group.push({ data: a.data, mimeType: a.mimeType || 'image/jpeg' });\n i += 1;\n }\n if (group.length > 0) {\n const parts = await resolveInboundImageContentParts({\n modelRef,\n cfg,\n userTextForContext: msg.content.trim() ? textBody : '',\n images: group,\n });\n messageContent.push(...parts);\n }\n } else {\n const fileBlock = formatInboundFileTextBlock(\n {\n type: att.type,\n mimeType: att.mimeType,\n name: att.name,\n size: att.size,\n workspaceRelativePath: att.workspaceRelativePath,\n },\n storageRootAbs,\n );\n messageContent.push({ type: 'text', text: fileBlock });\n i += 1;\n }\n }\n\n const hasText = messageContent.some((item) => item.type === 'text');\n const hasImage = messageContent.some((item) => item.type === 'image');\n if (hasImage && !hasText) {\n messageContent.unshift({ type: 'text', text: 'Please analyze the image(s) I sent.' });\n }\n\n if (messageContent.length === 0) {\n log.warn(\n { attachmentCount: msg.attachments.length },\n 'All attachments were skipped, falling back to text message',\n );\n return {\n role: 'user',\n content: textBody || '[Image attachment could not be processed]',\n timestamp: Date.now(),\n };\n }\n\n return {\n role: 'user',\n content: messageContent,\n timestamp: Date.now(),\n };\n }\n\n /**\n * Get the current agent model ID\n */\n getCurrentModel(): string {\n return this.modelManager.getCurrentModel();\n }\n\n /**\n * Check if agent is currently processing for a session\n */\n isProcessing(sessionKey: string): boolean {\n const agent = this.agentManager.getAgent(sessionKey);\n if (!agent) {\n return false;\n }\n return agent.state.messages.length > 0;\n }\n\n /**\n * Abort current agent execution for a session\n */\n abort(sessionKey: string): void {\n void abortEmbeddedRun(sessionKey);\n const agent = this.agentManager.getAgent(sessionKey);\n agent?.abort();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"agent-orchestrator.js","names":[],"sources":["../../../../src/agent/orchestration/agent-orchestrator.ts"],"sourcesContent":["/**\n * Agent Orchestrator - Coordinates Agent execution flow\n *\n * Manages the complete agent execution pipeline from message processing\n * to response generation.\n */\n\nimport type { Config } from '../../config/schema.js';\nimport type { InboundMessage } from '../../infra/bus/index.js';\nimport type { SessionConfigStore, SessionStore } from '../../session/index.js';\nimport type { SessionHydrator } from '../session/index.js';\nimport { resolveEffectiveThinkingLevel } from '../../session/thinking-resolve.js';\nimport type { ThinkLevel } from '../transcript/thinking-types.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { SessionContext } from '../session/session-context.js';\nimport type { AgentEventHandler } from './agent-event-handler.js';\nimport type { FeedbackCoordinator } from '../feedback/feedback-coordinator.js';\nimport type { AgentManager } from '../agent-manager.js';\nimport { createLogger } from '../../utils/logger.js';\nimport { extractAgentUserPlainText } from '../memory/user-message-text.js';\nimport { abortEmbeddedRun } from '../embedded/runs.js';\nimport { runEmbeddedTurnForSession } from '../embedded/run-for-session.js';\nimport type { EmbeddedStreamEvent } from '../embedded/types.js';\nimport { persistInboundAttachments } from '../../channels/attachments/inbound-persist.js';\nimport {\n buildTranscriptUserMessage,\n clearPendingTranscriptUserMessage,\n hydrateUserTurnForLlm,\n setPendingTranscriptUserMessage,\n} from '../inbound/attachment-pipeline.js';\nimport {\n DREAMING_SWEEP_TOKEN,\n DREAMING_LIGHT_SWEEP_TOKEN,\n DREAMING_REM_SWEEP_TOKEN,\n} from '../memory/dreaming/constants.js';\nimport { resolveDreamingConfig } from '../memory/dreaming/config.js';\nimport { runDreamingDeepPromotion } from '../memory/dreaming/deep-promotion.js';\nimport { appendDreamingEvent, type DreamingEvent } from '../memory/dreaming/events.js';\nimport { runLightSweep } from '../memory/dreaming/light-sweep.js';\nimport { runRemPatterns } from '../memory/dreaming/rem-patterns.js';\n\nconst log = createLogger('AgentOrchestrator');\n\nexport interface AgentOrchestratorConfig {\n agentManager: AgentManager;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n eventHandler: AgentEventHandler;\n feedbackCoordinator: FeedbackCoordinator;\n sessionConfigStore: SessionConfigStore;\n /** Per-session hydration (workspace override + model override) before the agent runs. */\n sessionHydrator: SessionHydrator;\n getThinkingDefault: () => ThinkLevel | undefined;\n /** Per-session default from merged `agents.list` / defaults (optional). */\n getThinkingDefaultForSession?: (sessionKey: string) => ThinkLevel | undefined;\n /** Default workspace root when no per-session resolver is set. */\n workspaceRoot: string;\n /** Per-agent workspace root for attachments (optional; defaults to `workspaceRoot`). */\n getWorkspaceRootForSession?: (sessionKey: string) => string;\n /** Agent home (`…/agents/<id>/`) for inbound/TTS files — keeps internal state out of the markdown workspace. */\n getAgentInternalStorageRootForSession?: (sessionKey: string) => string;\n /** Fire-and-forget after full session persist (e.g. LLM session title); not called from mid-turn snapshots. */\n enqueueAutoTitle?: (sessionKey: string) => void;\n /** For per-turn timeout via `agents.defaults.maxTaskDurationMs`. */\n getConfig?: () => Config | undefined;\n /** Channel streaming: token/tool events from pi embedded session. */\n onEmbeddedStreamEvent?: (sessionKey: string, event: EmbeddedStreamEvent) => void;\n /** Called after a successful embedded turn with assistant plain text. */\n onEmbeddedTurnComplete?: (sessionKey: string, lastAssistantText?: string) => void;\n}\n\nexport class AgentOrchestrator {\n private agentManager: AgentManager;\n private sessionStore: SessionStore;\n private modelManager: ModelManager;\n private eventHandler: AgentEventHandler;\n private feedbackCoordinator: FeedbackCoordinator;\n private sessionConfigStore: SessionConfigStore;\n private sessionHydrator: SessionHydrator;\n private getThinkingDefault: () => ThinkLevel | undefined;\n private getThinkingDefaultForSession?: (sessionKey: string) => ThinkLevel | undefined;\n private workspaceRoot: string;\n private getWorkspaceRootForSession?: (sessionKey: string) => string;\n private getAgentInternalStorageRootForSession: (sessionKey: string) => string;\n private enqueueAutoTitle?: (sessionKey: string) => void;\n private getConfig?: () => Config | undefined;\n private onEmbeddedStreamEvent?: (sessionKey: string, event: EmbeddedStreamEvent) => void;\n private onEmbeddedTurnComplete?: (sessionKey: string, lastAssistantText?: string) => void;\n\n constructor(config: AgentOrchestratorConfig) {\n this.agentManager = config.agentManager;\n this.sessionStore = config.sessionStore;\n this.modelManager = config.modelManager;\n this.eventHandler = config.eventHandler;\n this.feedbackCoordinator = config.feedbackCoordinator;\n this.sessionConfigStore = config.sessionConfigStore;\n this.sessionHydrator = config.sessionHydrator;\n this.getThinkingDefault = config.getThinkingDefault;\n this.getThinkingDefaultForSession = config.getThinkingDefaultForSession;\n this.workspaceRoot = config.workspaceRoot;\n this.getWorkspaceRootForSession = config.getWorkspaceRootForSession;\n this.getAgentInternalStorageRootForSession =\n config.getAgentInternalStorageRootForSession ??\n ((sk) => this.getWorkspaceRootForSession?.(sk) ?? this.workspaceRoot);\n this.enqueueAutoTitle = config.enqueueAutoTitle;\n this.getConfig = config.getConfig;\n this.onEmbeddedStreamEvent = config.onEmbeddedStreamEvent;\n this.onEmbeddedTurnComplete = config.onEmbeddedTurnComplete;\n }\n\n /**\n * Process a message through the agent orchestration pipeline\n */\n async process(msg: InboundMessage, context: SessionContext): Promise<void> {\n const { sessionKey } = context;\n\n log.debug({ sessionKey }, 'Processing message through agent orchestrator');\n\n await this.sessionHydrator.workspace(sessionKey);\n\n // Dreaming: short-circuit cron-triggered sweep tokens into maintenance runs.\n // This avoids spending LLM tokens for scheduled memory consolidation.\n if (\n typeof msg.content === 'string' &&\n (sessionKey.startsWith('cron:') || context.channel === 'cron')\n ) {\n const content = msg.content;\n const isDreamingSweep =\n content.includes(DREAMING_SWEEP_TOKEN) ||\n content.includes(DREAMING_LIGHT_SWEEP_TOKEN) ||\n content.includes(DREAMING_REM_SWEEP_TOKEN);\n\n if (isDreamingSweep) {\n const workspaceDir = this.agentManager.getResolvedWorkspaceForSession(sessionKey);\n const resolved = resolveDreamingConfig(this.getConfig?.());\n const t0 = Date.now();\n\n if (content.includes(DREAMING_LIGHT_SWEEP_TOKEN)) {\n const result = await runLightSweep({ workspaceDir, config: resolved.phases.light });\n const event: DreamingEvent = {\n timestamp: new Date().toISOString(), phase: 'light',\n ok: result.ok, reason: result.reason, durationMs: Date.now() - t0,\n scannedEntries: result.scannedEntries, newSignals: result.newSignals, deduped: result.deduped,\n };\n await appendDreamingEvent(workspaceDir, event);\n } else if (content.includes(DREAMING_REM_SWEEP_TOKEN)) {\n const result = await runRemPatterns({ workspaceDir, config: resolved.phases.rem });\n const event: DreamingEvent = {\n timestamp: new Date().toISOString(), phase: 'rem',\n ok: result.ok, reason: result.reason, durationMs: Date.now() - t0,\n patternsDiscovered: result.patternsDiscovered, entriesAnalyzed: result.entriesAnalyzed,\n };\n await appendDreamingEvent(workspaceDir, event);\n } else {\n const result = await runDreamingDeepPromotion({ workspaceDir, config: resolved.phases.deep });\n const event: DreamingEvent = {\n timestamp: new Date().toISOString(), phase: 'deep',\n ok: result.ok, reason: result.reason, durationMs: Date.now() - t0,\n candidates: result.candidates, applied: result.applied,\n };\n await appendDreamingEvent(workspaceDir, event);\n }\n return;\n }\n }\n\n try {\n await this.sessionHydrator.model(sessionKey);\n\n const channelSystemPrompt =\n typeof context.metadata?.channelSystemPrompt === 'string'\n ? context.metadata.channelSystemPrompt.trim()\n : '';\n if (channelSystemPrompt) {\n this.agentManager.getOrCreateAgent(sessionKey);\n this.agentManager.applyTurnChannelSystemPrompt(sessionKey, channelSystemPrompt);\n }\n\n const thinkingDefault =\n this.getThinkingDefaultForSession?.(sessionKey) ?? this.getThinkingDefault();\n const thinkingLevel = await resolveEffectiveThinkingLevel(\n this.sessionConfigStore,\n sessionKey,\n null,\n thinkingDefault,\n );\n this.agentManager.setThinkingLevel(sessionKey, thinkingLevel);\n\n const persistedAttachments = await persistInboundAttachments(msg.attachments);\n const modelRef = this.modelManager.getModelForSession(sessionKey);\n const userMessage = await buildTranscriptUserMessage({\n text: msg.content,\n prepared: persistedAttachments,\n sessionKey,\n modelRef,\n config: this.getConfig?.(),\n agentManager: this.agentManager,\n });\n setPendingTranscriptUserMessage(sessionKey, userMessage);\n\n const userPlainForMemory = extractAgentUserPlainText(userMessage);\n const userMessageForModel = await this.agentManager.applyMemoryPrefetchToUserMessage(\n userMessage,\n sessionKey,\n );\n\n const llmTurn = await hydrateUserTurnForLlm({\n message: userMessage,\n modelRef,\n });\n\n this.feedbackCoordinator.startTask();\n\n const turnResult = await (async () => {\n try {\n return await runEmbeddedTurnForSession({\n sessionKey,\n userMessage: userMessageForModel,\n llmImages: llmTurn.images,\n sessionStore: this.sessionStore,\n agentManager: this.agentManager,\n modelManager: this.modelManager,\n thinkingOverride: thinkingLevel,\n getConfig: this.getConfig,\n beforeTurn: () => this.agentManager.beginBackgroundReviewUserTurn(sessionKey),\n onEvent: (event) => this.onEmbeddedStreamEvent?.(sessionKey, event),\n });\n } finally {\n clearPendingTranscriptUserMessage(sessionKey, userMessage);\n }\n })();\n\n this.agentManager.afterAgentTurn(sessionKey, userPlainForMemory);\n this.agentManager.scheduleBackgroundReviewAfterUserTurn(sessionKey);\n\n if (turnResult.ok) {\n this.onEmbeddedTurnComplete?.(sessionKey, turnResult.lastAssistantText);\n this.enqueueAutoTitle?.(sessionKey);\n } else if (turnResult.errorMessage) {\n log.warn({ sessionKey, errorMessage: turnResult.errorMessage }, 'Embedded inbound turn failed');\n }\n\n this.feedbackCoordinator.endTask();\n\n } catch (error) {\n log.error({ err: error, sessionKey }, 'Error in agent orchestration');\n this.feedbackCoordinator.endTask();\n throw error;\n }\n }\n\n /**\n * Get the current agent model ID\n */\n getCurrentModel(): string {\n return this.modelManager.getCurrentModel();\n }\n\n /**\n * Check if agent is currently processing for a session\n */\n isProcessing(sessionKey: string): boolean {\n const agent = this.agentManager.getAgent(sessionKey);\n if (!agent) {\n return false;\n }\n return agent.state.messages.length > 0;\n }\n\n /**\n * Abort current agent execution for a session\n */\n abort(sessionKey: string): void {\n void abortEmbeddedRun(sessionKey);\n const agent = this.agentManager.getAgent(sessionKey);\n agent?.abort();\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export type ProviderSystemPromptSectionId = 'interaction_style' | 'tool_call_style' | 'execution_bias';
2
+ export type ProviderSystemPromptContribution = {
3
+ /** Cache-stable provider guidance inserted above the prompt cache boundary. */
4
+ stablePrefix?: string;
5
+ /** Provider guidance inserted below the cache boundary. */
6
+ dynamicSuffix?: string;
7
+ /** Whole-section replacements for selected core prompt sections. */
8
+ sectionOverrides?: Partial<Record<ProviderSystemPromptSectionId, string>>;
9
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,14 +1,4 @@
1
- import { Type } from "@sinclair/typebox";
2
1
  //#region src/agent/prompt/safety.ts
3
- Type.Object({
4
- allowExternalActions: Type.Optional(Type.Boolean({ default: false })),
5
- allowDangerousOperations: Type.Optional(Type.Boolean({ default: false })),
6
- requireConfirmationForDestructive: Type.Optional(Type.Boolean({ default: true })),
7
- allowedChannels: Type.Optional(Type.Array(Type.String())),
8
- blockedCommands: Type.Optional(Type.Array(Type.String())),
9
- maxFileSize: Type.Optional(Type.Number({ default: 10 * 1024 * 1024 })),
10
- maxShellTimeout: Type.Optional(Type.Number({ default: 300 }))
11
- });
12
2
  /**
13
3
  * Quick safety check for file operations
14
4
  */
@@ -1 +1 @@
1
- {"version":3,"file":"safety.js","names":[],"sources":["../../../../src/agent/prompt/safety.ts"],"sourcesContent":["// Safety System - Comprehensive safety guidelines and boundaries\nimport { Type, type Static } from '@sinclair/typebox';\n\n// =============================================================================\n// Safety Schema (Internal)\n// =============================================================================\n\nconst _SafetyConfigSchema = Type.Object({\n allowExternalActions: Type.Optional(Type.Boolean({ default: false })),\n allowDangerousOperations: Type.Optional(Type.Boolean({ default: false })),\n requireConfirmationForDestructive: Type.Optional(Type.Boolean({ default: true })),\n allowedChannels: Type.Optional(Type.Array(Type.String())),\n blockedCommands: Type.Optional(Type.Array(Type.String())),\n maxFileSize: Type.Optional(Type.Number({ default: 10 * 1024 * 1024 })), // 10MB\n maxShellTimeout: Type.Optional(Type.Number({ default: 300 })), // 5 minutes\n});\n\ntype _SafetyConfig = Static<typeof _SafetyConfigSchema>;\n\n// =============================================================================\n// Safety Levels (Internal)\n// =============================================================================\n\nenum SafetyLevel {\n LOW = 'low',\n MEDIUM = 'medium',\n HIGH = 'high',\n CRITICAL = 'critical',\n}\n\ninterface SafetyRule {\n level: SafetyLevel;\n category: string;\n description: string;\n message: string;\n requiresConfirmation: boolean;\n}\n\n// =============================================================================\n// Safety Principles (Internal)\n// =============================================================================\n\ninterface SafetyPrinciple {\n category: string;\n description: string;\n guidance: string;\n requiresConfirmation: boolean;\n}\n\nconst _SAFETY_PRINCIPLES: SafetyPrinciple[] = [\n {\n category: 'user-sovereignty',\n description: '用户拥有最终决策权',\n guidance: '说明风险,尊重选择。用 \"这将删除文件,继续吗?\" 而非 \"我不能删除文件。\"',\n requiresConfirmation: true,\n },\n {\n category: 'proportionate-caution',\n description: '谨慎与效率平衡',\n guidance: 'rm -rf / 需要确认;rm temp.txt 不需要。匹配风险等级与确认强度。',\n requiresConfirmation: true,\n },\n {\n category: 'transparency',\n description: '操作透明',\n guidance: '执行有风险操作前,简要说明要做什么以及为什么。',\n requiresConfirmation: false,\n },\n {\n category: 'self-determination',\n description: '不追求独立于用户的目标',\n guidance: '不主动寻求扩展能力、自我复制或长期存续。以用户当前需求为中心。',\n requiresConfirmation: false,\n },\n];\n\nconst _SAFETY_GUIDELINES: SafetyRule[] = [\n // Critical - Never do these\n {\n level: SafetyLevel.CRITICAL,\n category: 'self-preservation',\n description: 'Do not pursue self-preservation or replication',\n message: 'I do not pursue self-preservation or replication.',\n requiresConfirmation: false,\n },\n {\n level: SafetyLevel.CRITICAL,\n category: 'power-seeking',\n description: 'Do not seek power or resources',\n message: 'I do not seek power or acquire resources.',\n requiresConfirmation: false,\n },\n {\n level: SafetyLevel.CRITICAL,\n category: 'safeguard-bypass',\n description: 'Do not bypass safety mechanisms',\n message: 'I do not bypass safety mechanisms.',\n requiresConfirmation: false,\n },\n {\n level: SafetyLevel.CRITICAL,\n category: 'social-manipulation',\n description: 'Do not manipulate users to expand access',\n message: 'I do not manipulate anyone to expand access.',\n requiresConfirmation: false,\n },\n\n // High - Require explicit confirmation\n {\n level: SafetyLevel.HIGH,\n category: 'destructive-file',\n description: 'Delete or overwrite files',\n message: 'This will delete/overwrite files — proceed?',\n requiresConfirmation: true,\n },\n {\n level: SafetyLevel.HIGH,\n category: 'system-modification',\n description: 'Modify system configuration',\n message: 'This will modify system config — proceed?',\n requiresConfirmation: true,\n },\n {\n level: SafetyLevel.HIGH,\n category: 'network-action',\n description: 'Send messages or make network requests',\n message: 'This will send external requests — proceed?',\n requiresConfirmation: true,\n },\n {\n level: SafetyLevel.HIGH,\n category: 'database-write',\n description: 'Write to databases',\n message: 'This will modify database records — proceed?',\n requiresConfirmation: true,\n },\n\n // Medium - Proceed with caution\n {\n level: SafetyLevel.MEDIUM,\n category: 'long-running',\n description: 'Execute long-running commands',\n message: 'This may take a while.',\n requiresConfirmation: false,\n },\n {\n level: SafetyLevel.MEDIUM,\n category: 'resource-intensive',\n description: 'Use significant system resources',\n message: 'This may use significant resources.',\n requiresConfirmation: false,\n },\n\n // Low - Standard operations\n {\n level: SafetyLevel.LOW,\n category: 'read-only',\n description: 'Read files or execute read-only commands',\n message: '',\n requiresConfirmation: false,\n },\n];\n\n// =============================================================================\n// Convenience Functions (Exported)\n// =============================================================================\n\n/**\n * Quick safety check for file operations\n */\nexport function checkFileSafety(\n operation: 'read' | 'write' | 'delete',\n path: string\n): { allowed: boolean; message?: string } {\n // Check for sensitive paths\n const sensitivePaths = [\n '/etc/passwd',\n '/etc/shadow',\n '/root/.ssh',\n '/home/*/.ssh',\n '~/.aws',\n '~/.bashrc',\n '~/.profile',\n ];\n\n for (const sp of sensitivePaths) {\n if (path.includes(sp)) {\n return {\n allowed: false,\n message: `Cannot ${operation} sensitive path: ${path}`,\n };\n }\n }\n\n return { allowed: true };\n}\n\n"],"mappings":";;AAO4B,KAAK,OAAO;CACtC,sBAAsB,KAAK,SAAS,KAAK,QAAQ,EAAE,SAAS,OAAO,CAAC,CAAC;CACrE,0BAA0B,KAAK,SAAS,KAAK,QAAQ,EAAE,SAAS,OAAO,CAAC,CAAC;CACzE,mCAAmC,KAAK,SAAS,KAAK,QAAQ,EAAE,SAAS,MAAM,CAAC,CAAC;CACjF,iBAAiB,KAAK,SAAS,KAAK,MAAM,KAAK,QAAQ,CAAC,CAAC;CACzD,iBAAiB,KAAK,SAAS,KAAK,MAAM,KAAK,QAAQ,CAAC,CAAC;CACzD,aAAa,KAAK,SAAS,KAAK,OAAO,EAAE,SAAS,KAAK,OAAO,MAAM,CAAC,CAAC;CACtE,iBAAiB,KAAK,SAAS,KAAK,OAAO,EAAE,SAAS,KAAK,CAAC,CAAC;CAC9D,CAAC;;;;AA2JF,SAAgB,gBACd,WACA,MACwC;AAYxC,MAAK,MAAM,MAAM;EATf;EACA;EACA;EACA;EACA;EACA;EACA;EAG6B,CAC7B,KAAI,KAAK,SAAS,GAAG,CACnB,QAAO;EACL,SAAS;EACT,SAAS,UAAU,UAAU,mBAAmB;EACjD;AAIL,QAAO,EAAE,SAAS,MAAM"}
1
+ {"version":3,"file":"safety.js","names":[],"sources":["../../../../src/agent/prompt/safety.ts"],"sourcesContent":["// Safety System - Quick safety check for file operations\n\n/**\n * Quick safety check for file operations\n */\nexport function checkFileSafety(\n operation: 'read' | 'write' | 'delete',\n path: string\n): { allowed: boolean; message?: string } {\n // Check for sensitive paths\n const sensitivePaths = [\n '/etc/passwd',\n '/etc/shadow',\n '/root/.ssh',\n '/home/*/.ssh',\n '~/.aws',\n '~/.bashrc',\n '~/.profile',\n ];\n\n for (const sp of sensitivePaths) {\n if (path.includes(sp)) {\n return {\n allowed: false,\n message: `Cannot ${operation} sensitive path: ${path}`,\n };\n }\n }\n\n return { allowed: true };\n}\n\n"],"mappings":";;;;AAKA,SAAgB,gBACd,WACA,MACwC;AAYxC,MAAK,MAAM,MAAM;EATf;EACA;EACA;EACA;EACA;EACA;EACA;EAG6B,CAC7B,KAAI,KAAK,SAAS,GAAG,CACnB,QAAO;EACL,SAAS;EACT,SAAS,UAAU,UAAU,mBAAmB;EACjD;AAIL,QAAO,EAAE,SAAS,MAAM"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Sanitize untrusted strings before embedding them into an LLM prompt.
3
+ *
4
+ * Strips control/format characters that could break prompt structure or inject instructions.
5
+ */
6
+ export declare function sanitizeForPromptLiteral(value: string): string;