@xopcai/xopc 0.0.112 → 0.0.114

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 (351) hide show
  1. package/dist/browser-ext/icons/icon-128.png +0 -0
  2. package/dist/browser-ext/icons/icon-16.png +0 -0
  3. package/dist/browser-ext/icons/icon-32.png +0 -0
  4. package/dist/browser-ext/icons/icon-48.png +0 -0
  5. package/dist/browser-ext/manifest.json +1 -1
  6. package/dist/extensions/telegram/xopc.extension.json +1 -1
  7. package/dist/extensions/weixin/src/delivery-to.d.ts +3 -3
  8. package/dist/extensions/weixin/src/delivery-to.js.map +1 -1
  9. package/dist/gateway/static/root/apple-touch-icon.png +0 -0
  10. package/dist/gateway/static/root/assets/{agent-avatar-dicebear-EhRp-ZMa.js → agent-avatar-dicebear-Fp-p7bP1.js} +1 -1
  11. package/dist/gateway/static/root/assets/agent-browser-settings-page-CWh7trjd.js +6 -0
  12. package/dist/gateway/static/root/assets/agent-defaults-tabbed-page-kC8OuLoj.js +1 -0
  13. package/dist/gateway/static/root/assets/agents-DJWG26Si.js +71 -0
  14. package/dist/gateway/static/root/assets/{app-management-settings-panel-BIy7KEsM.js → app-management-settings-panel-v24i25Oj.js} +1 -1
  15. package/dist/gateway/static/root/assets/{appearance-settings-DGQG8NwA.js → appearance-settings-ycbdJRuZ.js} +1 -1
  16. package/dist/gateway/static/root/assets/apps-page-0V3DXZ7J.js +1 -0
  17. package/dist/gateway/static/root/assets/{archive-plugin-uuQPupqx.js → archive-plugin-CJzSADJo.js} +1 -1
  18. package/dist/gateway/static/root/assets/{attachment-load-DBkv0cAv.js → attachment-load-BTAB4zGS.js} +1 -1
  19. package/dist/gateway/static/root/assets/{binary-plugins-DxxN_VAR.js → binary-plugins-DXR1-mct.js} +1 -1
  20. package/dist/gateway/static/root/assets/{block-editor-CwnUlRmO.js → block-editor-CzQri9h2.js} +44 -44
  21. package/dist/gateway/static/root/assets/channels-settings-CIKOkYdO.js +1 -0
  22. package/dist/gateway/static/root/assets/{config-api-BD-NcD5d.js → config-api-DRqjEvAx.js} +1 -1
  23. package/dist/gateway/static/root/assets/connectors-settings-ClrDS5jH.js +2 -0
  24. package/dist/gateway/static/root/assets/{cron-api-C5DoipZA.js → cron-api-BXaZXv1o.js} +1 -1
  25. package/dist/gateway/static/root/assets/cron-page-Chi_4u4P.js +3 -0
  26. package/dist/gateway/static/root/assets/dreaming-settings-B0_rs2tj.js +1 -0
  27. package/dist/gateway/static/root/assets/{extension-debug-page-BDHA9o_e.js → extension-debug-page-DSkqMYo7.js} +1 -1
  28. package/dist/gateway/static/root/assets/{extension-page-DmDLVSPE.js → extension-page-h2dGDbMN.js} +1 -1
  29. package/dist/gateway/static/root/assets/{extension-settings-page-1124aHw_.js → extension-settings-page-hN99RhOi.js} +1 -1
  30. package/dist/gateway/static/root/assets/{fetch-DOe56Wmy.js → fetch-D9QqUtzZ.js} +1 -1
  31. package/dist/gateway/static/root/assets/{format-interval-label-tsMSvBSR.js → format-interval-label-D6E687rG.js} +1 -1
  32. package/dist/gateway/static/root/assets/{gateway-config-api-CsD4FC9D.js → gateway-config-api-CLt_SKTC.js} +1 -1
  33. package/dist/gateway/static/root/assets/{gateway-settings-HQ8FBhY2.js → gateway-settings-1PDi4lI0.js} +1 -1
  34. package/dist/gateway/static/root/assets/{gateway-startup-retry-C1zg7pH3.js → gateway-startup-retry-N7Z_sz7p.js} +1 -1
  35. package/dist/gateway/static/root/assets/goal-detail-page-D_wwXL-t.js +1 -0
  36. package/dist/gateway/static/root/assets/goals-page-DZ2toZ8y.js +2 -0
  37. package/dist/gateway/static/root/assets/{goals-settings-B5nI6lgz.js → goals-settings-LFlwtIKE.js} +1 -1
  38. package/dist/gateway/static/root/assets/{heartbeat-config-api-CIv42nJD.js → heartbeat-config-api-DGPadTw-.js} +1 -1
  39. package/dist/gateway/static/root/assets/{heartbeat-settings-DFcVuJ3M.js → heartbeat-settings-BHHAs8ZX.js} +1 -1
  40. package/dist/gateway/static/root/assets/index-DefIdW9q.js +122 -0
  41. package/dist/gateway/static/root/assets/index-DzmWBkq-.css +1 -0
  42. package/dist/gateway/static/root/assets/{interval-presets-DGP3S7OB.js → interval-presets-CEklPQ_r.js} +1 -1
  43. package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-fETaDYQP.js → keyboard-shortcuts-settings-Ce_aQ8Iv.js} +1 -1
  44. package/dist/gateway/static/root/assets/logs-page-j_D2QTuQ.js +2 -0
  45. package/dist/gateway/static/root/assets/{markdown-split-CgeBJMOD.js → markdown-split-BMz3xPXO.js} +1 -1
  46. package/dist/gateway/static/root/assets/{media-plugins-C30GzlV2.js → media-plugins-BalOSoz9.js} +1 -1
  47. package/dist/gateway/static/root/assets/messages-BN_WiUg0.js +3 -0
  48. package/dist/gateway/static/root/assets/models-hub-panel-CMH_TmZz.js +2 -0
  49. package/dist/gateway/static/root/assets/note-detail-page-O18BrQPq.js +2 -0
  50. package/dist/gateway/static/root/assets/{note-image-lightbox-t1g0qZYh.js → note-image-lightbox-C5D1Q5mY.js} +1 -1
  51. package/dist/gateway/static/root/assets/notes-page-B_ZYCmY9.js +1 -0
  52. package/dist/gateway/static/root/assets/{preview-open-alternatives-DeWp9x5g.js → preview-open-alternatives-CYFR5U9l.js} +1 -1
  53. package/dist/gateway/static/root/assets/remote-access-hub-CwKgWixY.js +1 -0
  54. package/dist/gateway/static/root/assets/{save-bar-controls-BOaAOJI3.js → save-bar-controls-BQ79Z5IP.js} +1 -1
  55. package/dist/gateway/static/root/assets/{schedule-field-BLWLKqgw.js → schedule-field-Bgk6Emue.js} +1 -1
  56. package/dist/gateway/static/root/assets/{schema-form-plqRFYT8.js → schema-form-DzS36TMa.js} +1 -1
  57. package/dist/gateway/static/root/assets/sessions-page-C4fcD-7B.js +1 -0
  58. package/dist/gateway/static/root/assets/settings-advanced-gate-Ad2OJ86w.js +1 -0
  59. package/dist/gateway/static/root/assets/{settings-collapsible-section-BP5zRrDI.js → settings-collapsible-section-CdjFnSAy.js} +1 -1
  60. package/dist/gateway/static/root/assets/{settings-form-section-Bv8MZplG.js → settings-form-section-DZLy_exu.js} +1 -1
  61. package/dist/gateway/static/root/assets/settings-page-D2BMY5wo.js +1 -0
  62. package/dist/gateway/static/root/assets/setup-status-panel-aWwxl_E1.js +1 -0
  63. package/dist/gateway/static/root/assets/{share-preview-page-4OX1YCgp.js → share-preview-page-_qRDr4g_.js} +1 -1
  64. package/dist/gateway/static/root/assets/shares-settings-DUE83uIf.js +1 -0
  65. package/dist/gateway/static/root/assets/skill-reload-api-mWKJcrog.js +1 -0
  66. package/dist/gateway/static/root/assets/skills-page-0Sx1Q58n.js +2 -0
  67. package/dist/gateway/static/root/assets/{system-settings-panel-CGy4f_s2.js → system-settings-panel-hqaBF5aG.js} +1 -1
  68. package/dist/gateway/static/root/assets/use-agent-defaults-form-zbVIQLOA.js +1 -0
  69. package/dist/gateway/static/root/assets/{use-channel-catalog-CjGHnDvN.js → use-channel-catalog-CbXq97rT.js} +1 -1
  70. package/dist/gateway/static/root/assets/{use-settings-tab-guard-BMMFa0oA.js → use-settings-tab-guard-DwS3drWK.js} +1 -1
  71. package/dist/gateway/static/root/assets/voice-api-key-field-D6HR0hR2.js +1 -0
  72. package/dist/gateway/static/root/assets/{voice-player-store-D5vzWwls.js → voice-player-store-DSuOd9zy.js} +1 -1
  73. package/dist/gateway/static/root/assets/workflow-page.utils-C0Uq3xyq.js +1 -0
  74. package/dist/gateway/static/root/assets/workflows-page-DW0fmWhQ.js +18 -0
  75. package/dist/gateway/static/root/favicon-16x16.png +0 -0
  76. package/dist/gateway/static/root/favicon-32x32.png +0 -0
  77. package/dist/gateway/static/root/favicon-icon.svg +1 -0
  78. package/dist/gateway/static/root/favicon.ico +0 -0
  79. package/dist/gateway/static/root/favicon.png +0 -0
  80. package/dist/gateway/static/root/favicon.svg +24 -17
  81. package/dist/gateway/static/root/index.html +5 -5
  82. package/dist/gateway/static/root/logo-dark.svg +23 -15
  83. package/dist/gateway/static/root/logo.svg +23 -16
  84. package/dist/package.js +1 -1
  85. package/dist/src/agent/agent-manager.d.ts +8 -0
  86. package/dist/src/agent/agent-manager.js +57 -22
  87. package/dist/src/agent/agent-manager.js.map +1 -1
  88. package/dist/src/agent/bootstrap/project-agents-context.d.ts +2 -0
  89. package/dist/src/agent/bootstrap/project-agents-context.js +32 -0
  90. package/dist/src/agent/bootstrap/project-agents-context.js.map +1 -0
  91. package/dist/src/agent/goals/persistent-goal-service.js +2 -4
  92. package/dist/src/agent/goals/persistent-goal-service.js.map +1 -1
  93. package/dist/src/agent/inbound/inbound-loop.js +5 -1
  94. package/dist/src/agent/inbound/inbound-loop.js.map +1 -1
  95. package/dist/src/agent/inbound/turn-dispatcher.d.ts +2 -2
  96. package/dist/src/agent/inbound/turn-dispatcher.js +2 -2
  97. package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
  98. package/dist/src/agent/mcp/bundle-mcp-materialize.d.ts +27 -0
  99. package/dist/src/agent/mcp/bundle-mcp-materialize.js +49 -1
  100. package/dist/src/agent/mcp/bundle-mcp-materialize.js.map +1 -1
  101. package/dist/src/agent/mcp/bundle-mcp-runtime.js +78 -3
  102. package/dist/src/agent/mcp/bundle-mcp-runtime.js.map +1 -1
  103. package/dist/src/agent/mcp/bundle-mcp-types.d.ts +19 -0
  104. package/dist/src/agent/service/btw-query.js +11 -3
  105. package/dist/src/agent/service/btw-query.js.map +1 -1
  106. package/dist/src/agent/service/parse-outbound-session-key.js +13 -8
  107. package/dist/src/agent/service/parse-outbound-session-key.js.map +1 -1
  108. package/dist/src/agent/service/process-direct-one-shot.d.ts +2 -2
  109. package/dist/src/agent/service/process-direct-one-shot.js +1 -1
  110. package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
  111. package/dist/src/agent/service/process-direct-streaming.d.ts +2 -2
  112. package/dist/src/agent/service/process-direct-streaming.js +10 -5
  113. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  114. package/dist/src/agent/service.d.ts +1 -1
  115. package/dist/src/agent/service.js +9 -5
  116. package/dist/src/agent/service.js.map +1 -1
  117. package/dist/src/agent/service.types.d.ts +5 -0
  118. package/dist/src/agent/skills/filesystem-watcher.js +4 -3
  119. package/dist/src/agent/skills/filesystem-watcher.js.map +1 -1
  120. package/dist/src/agent/skills/index.js +2 -1
  121. package/dist/src/agent/skills/index.js.map +1 -1
  122. package/dist/src/agent/skills/skill-manage-ops.js +2 -1
  123. package/dist/src/agent/skills/skill-manage-ops.js.map +1 -1
  124. package/dist/src/agent/skills/types.d.ts +1 -1
  125. package/dist/src/agent/skills/watcher.js +2 -5
  126. package/dist/src/agent/skills/watcher.js.map +1 -1
  127. package/dist/src/agent/skills/workspace-skills-dir.d.ts +1 -0
  128. package/dist/src/agent/skills/workspace-skills-dir.js +11 -0
  129. package/dist/src/agent/skills/workspace-skills-dir.js.map +1 -0
  130. package/dist/src/agent/tool-manuals/browser.d.ts +1 -1
  131. package/dist/src/agent/tool-manuals/browser.js +62 -3
  132. package/dist/src/agent/tool-manuals/browser.js.map +1 -1
  133. package/dist/src/agent/tools/browser/tool/browser-use-tool.d.ts +4 -1
  134. package/dist/src/agent/tools/browser/tool/browser-use-tool.js +13 -4
  135. package/dist/src/agent/tools/browser/tool/browser-use-tool.js.map +1 -1
  136. package/dist/src/agent/tools/browser/tool/schemas.d.ts +0 -2
  137. package/dist/src/agent/tools/browser/tool/schemas.js +1 -2
  138. package/dist/src/agent/tools/browser/tool/schemas.js.map +1 -1
  139. package/dist/src/agent/tools/factory.js +1 -10
  140. package/dist/src/agent/tools/factory.js.map +1 -1
  141. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  142. package/dist/src/agent/tools/skill-manage-tool.js.map +1 -1
  143. package/dist/src/agent/workflow/subagent-runner.js +18 -1
  144. package/dist/src/agent/workflow/subagent-runner.js.map +1 -1
  145. package/dist/src/ai-assist/text-assist.d.ts +3 -0
  146. package/dist/src/ai-assist/text-assist.js +25 -7
  147. package/dist/src/ai-assist/text-assist.js.map +1 -1
  148. package/dist/src/browser/actions/extension-registry-bridge.js +91 -7
  149. package/dist/src/browser/actions/extension-registry-bridge.js.map +1 -1
  150. package/dist/src/browser/actions/registry.js +383 -7
  151. package/dist/src/browser/actions/registry.js.map +1 -1
  152. package/dist/src/browser/actions/types.d.ts +11 -3
  153. package/dist/src/browser/index.js +2 -2
  154. package/dist/src/browser/pipeline/output.d.ts +18 -16
  155. package/dist/src/browser/pipeline/output.js +28 -20
  156. package/dist/src/browser/pipeline/output.js.map +1 -1
  157. package/dist/src/browser/pipeline/runner.d.ts +7 -14
  158. package/dist/src/browser/pipeline/runner.js +349 -102
  159. package/dist/src/browser/pipeline/runner.js.map +1 -1
  160. package/dist/src/browser/pipeline/schema.d.ts +1 -0
  161. package/dist/src/browser/pipeline/schema.js +35 -58
  162. package/dist/src/browser/pipeline/schema.js.map +1 -1
  163. package/dist/src/browser/pipeline/source.d.ts +2 -0
  164. package/dist/src/browser/pipeline/source.js +10 -1
  165. package/dist/src/browser/pipeline/source.js.map +1 -1
  166. package/dist/src/browser/pipeline/template.d.ts +10 -2
  167. package/dist/src/browser/pipeline/template.js +50 -8
  168. package/dist/src/browser/pipeline/template.js.map +1 -1
  169. package/dist/src/channels/plugins/types.adapters.d.ts +2 -2
  170. package/dist/src/chat-commands/builtins/config.js +3 -5
  171. package/dist/src/chat-commands/builtins/config.js.map +1 -1
  172. package/dist/src/chat-commands/context.d.ts +5 -0
  173. package/dist/src/chat-commands/context.js +18 -9
  174. package/dist/src/chat-commands/context.js.map +1 -1
  175. package/dist/src/chat-commands/index.d.ts +1 -1
  176. package/dist/src/chat-commands/index.js +2 -2
  177. package/dist/src/chat-commands/index.js.map +1 -1
  178. package/dist/src/chat-commands/processor.js +22 -21
  179. package/dist/src/chat-commands/processor.js.map +1 -1
  180. package/dist/src/chat-commands/session-key.d.ts +3 -15
  181. package/dist/src/chat-commands/session-key.js +3 -49
  182. package/dist/src/chat-commands/session-key.js.map +1 -1
  183. package/dist/src/chat-commands/types.d.ts +4 -0
  184. package/dist/src/cli/commands/browser-cli-helpers.js +11 -5
  185. package/dist/src/cli/commands/browser-cli-helpers.js.map +1 -1
  186. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  187. package/dist/src/cli/commands/doctor/checks/session-integrity.js +8 -8
  188. package/dist/src/cli/commands/doctor/checks/session-integrity.js.map +1 -1
  189. package/dist/src/config/rules.js +5 -0
  190. package/dist/src/config/rules.js.map +1 -1
  191. package/dist/src/connectors/builtin-catalog.js +180 -1
  192. package/dist/src/connectors/builtin-catalog.js.map +1 -1
  193. package/dist/src/connectors/health.js +18 -9
  194. package/dist/src/connectors/health.js.map +1 -1
  195. package/dist/src/connectors/types.d.ts +19 -0
  196. package/dist/src/connectors/usage.js +12 -4
  197. package/dist/src/connectors/usage.js.map +1 -1
  198. package/dist/src/cron/executor.js.map +1 -1
  199. package/dist/src/cron/types.d.ts +2 -2
  200. package/dist/src/extensions/types/tui.d.ts +2 -0
  201. package/dist/src/gateway/heartbeat/service.js +13 -1
  202. package/dist/src/gateway/heartbeat/service.js.map +1 -1
  203. package/dist/src/gateway/hono/app.js +13 -4
  204. package/dist/src/gateway/hono/app.js.map +1 -1
  205. package/dist/src/gateway/hono/oauth-async.js +1 -1
  206. package/dist/src/gateway/hono/routes/agent-stream.js +6 -6
  207. package/dist/src/gateway/hono/routes/agent-stream.js.map +1 -1
  208. package/dist/src/gateway/hono/routes/config-patch/gateway.d.ts +1 -1
  209. package/dist/src/gateway/hono/routes/config-patch/gateway.js +6 -0
  210. package/dist/src/gateway/hono/routes/config-patch/gateway.js.map +1 -1
  211. package/dist/src/gateway/hono/routes/goals.js +16 -3
  212. package/dist/src/gateway/hono/routes/goals.js.map +1 -1
  213. package/dist/src/gateway/hono/routes/logs.js +1 -0
  214. package/dist/src/gateway/hono/routes/logs.js.map +1 -1
  215. package/dist/src/gateway/hono/routes/mcp.js +27 -8
  216. package/dist/src/gateway/hono/routes/mcp.js.map +1 -1
  217. package/dist/src/gateway/hono/routes/notes.js +14 -2
  218. package/dist/src/gateway/hono/routes/notes.js.map +1 -1
  219. package/dist/src/gateway/hono/routes/sessions.js +62 -4
  220. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  221. package/dist/src/gateway/hono/routes/workspace.js +3 -66
  222. package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
  223. package/dist/src/gateway/hono/sse.d.ts +2 -2
  224. package/dist/src/gateway/hono/sse.js +7 -15
  225. package/dist/src/gateway/hono/sse.js.map +1 -1
  226. package/dist/src/gateway/resolve-webchat-session-key.d.ts +2 -6
  227. package/dist/src/gateway/resolve-webchat-session-key.js +5 -31
  228. package/dist/src/gateway/resolve-webchat-session-key.js.map +1 -1
  229. package/dist/src/gateway/service/agent-runner.d.ts +2 -3
  230. package/dist/src/gateway/service/agent-runner.js +9 -22
  231. package/dist/src/gateway/service/agent-runner.js.map +1 -1
  232. package/dist/src/gateway/service/run-gateway-agent.js +9 -7
  233. package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
  234. package/dist/src/gateway/service/sessions-api.d.ts +9 -0
  235. package/dist/src/gateway/service/sessions-api.js +11 -0
  236. package/dist/src/gateway/service/sessions-api.js.map +1 -1
  237. package/dist/src/gateway/service.d.ts +1 -1
  238. package/dist/src/gateway/service.js +19 -5
  239. package/dist/src/gateway/service.js.map +1 -1
  240. package/dist/src/gateway/workspace-file-search.d.ts +9 -0
  241. package/dist/src/gateway/workspace-file-search.js +76 -0
  242. package/dist/src/gateway/workspace-file-search.js.map +1 -0
  243. package/dist/src/goals/goal-notifications.d.ts +2 -0
  244. package/dist/src/goals/goal-notifications.js +16 -11
  245. package/dist/src/goals/goal-notifications.js.map +1 -1
  246. package/dist/src/goals/goal-service.js +1 -6
  247. package/dist/src/goals/goal-service.js.map +1 -1
  248. package/dist/src/project-trust/trust-store.js +2 -0
  249. package/dist/src/project-trust/trust-store.js.map +1 -1
  250. package/dist/src/session/index.d.ts +1 -1
  251. package/dist/src/session/index.js +2 -2
  252. package/dist/src/session/init-session-turn.js +6 -9
  253. package/dist/src/session/init-session-turn.js.map +1 -1
  254. package/dist/src/session/manager.d.ts +5 -1
  255. package/dist/src/session/manager.js +5 -2
  256. package/dist/src/session/manager.js.map +1 -1
  257. package/dist/src/session/reset-type.d.ts +0 -2
  258. package/dist/src/session/reset-type.js +3 -9
  259. package/dist/src/session/reset-type.js.map +1 -1
  260. package/dist/src/session/resolve-session.js +9 -20
  261. package/dist/src/session/resolve-session.js.map +1 -1
  262. package/dist/src/session/store-reader.d.ts +5 -0
  263. package/dist/src/session/store-reader.js +1 -0
  264. package/dist/src/session/store.d.ts +11 -3
  265. package/dist/src/session/store.js +29 -13
  266. package/dist/src/session/store.js.map +1 -1
  267. package/dist/src/session/types.d.ts +2 -4
  268. package/dist/src/session/types.js.map +1 -1
  269. package/dist/src/storage/sqlite/index.d.ts +2 -2
  270. package/dist/src/storage/sqlite/index.js +3 -3
  271. package/dist/src/storage/sqlite/migrations/010_session_id_identity.sql +30 -0
  272. package/dist/src/storage/sqlite/migrations/runner.d.ts +2 -2
  273. package/dist/src/storage/sqlite/migrations/runner.js +4 -4
  274. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  275. package/dist/src/storage/sqlite/migrations/runner.ts +2 -2
  276. package/dist/src/storage/sqlite/row-mappers.d.ts +4 -4
  277. package/dist/src/storage/sqlite/row-mappers.js +11 -14
  278. package/dist/src/storage/sqlite/row-mappers.js.map +1 -1
  279. package/dist/src/storage/sqlite/schema.js +3 -3
  280. package/dist/src/storage/sqlite/schema.js.map +1 -1
  281. package/dist/src/storage/sqlite/schema.sql +37 -10
  282. package/dist/src/storage/sqlite/session-metadata.d.ts +2 -7
  283. package/dist/src/storage/sqlite/session-metadata.js +18 -72
  284. package/dist/src/storage/sqlite/session-metadata.js.map +1 -1
  285. package/dist/src/storage/sqlite/session-repository.d.ts +6 -5
  286. package/dist/src/storage/sqlite/session-repository.js +34 -40
  287. package/dist/src/storage/sqlite/session-repository.js.map +1 -1
  288. package/dist/src/storage/sqlite/transcript-repository.d.ts +2 -2
  289. package/dist/src/storage/sqlite/transcript-repository.js +52 -52
  290. package/dist/src/storage/sqlite/transcript-repository.js.map +1 -1
  291. package/dist/src/tui/backends/embedded-backend.d.ts +11 -4
  292. package/dist/src/tui/backends/embedded-backend.js +27 -1
  293. package/dist/src/tui/backends/embedded-backend.js.map +1 -1
  294. package/dist/src/tui/backends/gateway-sse-backend.d.ts +4 -1
  295. package/dist/src/tui/backends/gateway-sse-backend.js +21 -2
  296. package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
  297. package/dist/src/tui/extension-host/autocomplete.js +13 -10
  298. package/dist/src/tui/extension-host/autocomplete.js.map +1 -1
  299. package/dist/src/tui/extension-host/runtime.d.ts +7 -0
  300. package/dist/src/tui/extension-host/runtime.js +6 -4
  301. package/dist/src/tui/extension-host/runtime.js.map +1 -1
  302. package/dist/src/tui/tui-backend.d.ts +9 -0
  303. package/dist/src/tui/tui-commands.js +85 -6
  304. package/dist/src/tui/tui-commands.js.map +1 -1
  305. package/dist/src/tui/tui-file-autocomplete.d.ts +5 -0
  306. package/dist/src/tui/tui-file-autocomplete.js +26 -0
  307. package/dist/src/tui/tui-file-autocomplete.js.map +1 -0
  308. package/dist/src/tui/tui-picker-overlay.js +1 -1
  309. package/dist/src/tui/tui.js +29 -11
  310. package/dist/src/tui/tui.js.map +1 -1
  311. package/dist/src/utils/logger/context.d.ts +2 -2
  312. package/dist/src/utils/logger/context.js +2 -1
  313. package/dist/src/utils/logger/context.js.map +1 -1
  314. package/dist/src/utils/logger/log-store.js +1 -0
  315. package/dist/src/utils/logger/log-store.js.map +1 -1
  316. package/dist/src/utils/logger/pino-record.js +3 -0
  317. package/dist/src/utils/logger/pino-record.js.map +1 -1
  318. package/dist/src/utils/logger/types.d.ts +5 -0
  319. package/dist/src/utils/logger/types.js.map +1 -1
  320. package/dist/src/workflows/service/workflow-session-bridge.js +13 -1
  321. package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -1
  322. package/package.json +1 -1
  323. package/dist/gateway/static/root/assets/agent-browser-settings-page-H4foVCiS.js +0 -6
  324. package/dist/gateway/static/root/assets/agent-defaults-tabbed-page-DDNw2oNU.js +0 -1
  325. package/dist/gateway/static/root/assets/agents-E_X2kzS1.js +0 -71
  326. package/dist/gateway/static/root/assets/apps-page-BZLvInLr.js +0 -1
  327. package/dist/gateway/static/root/assets/channels-settings-BHvUKlzw.js +0 -1
  328. package/dist/gateway/static/root/assets/connectors-settings-VBzXcEXL.js +0 -2
  329. package/dist/gateway/static/root/assets/cron-page-BLTpdm34.js +0 -3
  330. package/dist/gateway/static/root/assets/dreaming-settings-DSVhXTPq.js +0 -1
  331. package/dist/gateway/static/root/assets/goal-detail-page-BG3Onph0.js +0 -1
  332. package/dist/gateway/static/root/assets/goals-page-DgeKn8zL.js +0 -2
  333. package/dist/gateway/static/root/assets/index-5A_ZaABy.css +0 -1
  334. package/dist/gateway/static/root/assets/index-BTmbp2AU.js +0 -121
  335. package/dist/gateway/static/root/assets/logs-page-B1-b27lK.js +0 -2
  336. package/dist/gateway/static/root/assets/messages-BUUfPaZ9.js +0 -3
  337. package/dist/gateway/static/root/assets/models-hub-panel-DPcTS1-u.js +0 -2
  338. package/dist/gateway/static/root/assets/note-detail-page-xeGb4owe.js +0 -2
  339. package/dist/gateway/static/root/assets/notes-page-C_TdnKBe.js +0 -1
  340. package/dist/gateway/static/root/assets/remote-access-hub-BtkPmJIN.js +0 -1
  341. package/dist/gateway/static/root/assets/sessions-page-DOUjxzJm.js +0 -1
  342. package/dist/gateway/static/root/assets/settings-advanced-gate-qK_bnTrw.js +0 -1
  343. package/dist/gateway/static/root/assets/settings-page-DmDfE1ww.js +0 -1
  344. package/dist/gateway/static/root/assets/setup-status-panel-BEFwNcIZ.js +0 -1
  345. package/dist/gateway/static/root/assets/shares-settings-CbKmJZot.js +0 -1
  346. package/dist/gateway/static/root/assets/skill-reload-api-YdtnUHtK.js +0 -1
  347. package/dist/gateway/static/root/assets/skills-page-DwW1IzQ0.js +0 -2
  348. package/dist/gateway/static/root/assets/use-agent-defaults-form-BMuMvzl_.js +0 -1
  349. package/dist/gateway/static/root/assets/voice-api-key-field-DnRbhMNq.js +0 -1
  350. package/dist/gateway/static/root/assets/workflow-page.utils-tjGpRVnw.js +0 -1
  351. package/dist/gateway/static/root/assets/workflows-page-DqqHKGj2.js +0 -18
@@ -1 +1 @@
1
- {"version":3,"file":"bundle-mcp-runtime.js","names":[],"sources":["../../../../src/agent/mcp/bundle-mcp-runtime.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport Ajv2020 from \"ajv/dist/2020.js\";\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { AjvJsonSchemaValidator } from \"@modelcontextprotocol/sdk/validation/ajv-provider.js\";\nimport type {\n JsonSchemaType,\n JsonSchemaValidator,\n jsonSchemaValidator,\n} from \"@modelcontextprotocol/sdk/validation/types.js\";\nimport type { Config } from \"../../config/schema.js\";\nimport { createLogger } from \"../../utils/logger.js\";\nconst log = createLogger(\"Mcp:Bundle\");\nimport { resolveGlobalSingleton } from \"../../utils/global-singleton.js\";\nimport { redactSensitiveUrlLikeString } from \"../../utils/redact-sensitive-url.js\";\nimport { normalizeOptionalString } from \"../../utils/string-coerce.js\";\nimport { loadEmbeddedMcpConfig } from \"./embedded-mcp.js\";\nimport { resolveConnectorSecretReferences } from \"../../connectors/secret-store.js\";\nimport { isMcpConfigRecord } from \"./mcp-config-shared.js\";\nimport { resolveMcpTransport } from \"./mcp-transport.js\";\nimport { sanitizeServerName } from \"./bundle-mcp-names.js\";\nimport type {\n McpCatalogTool,\n McpServerCatalog,\n McpToolCatalog,\n SessionMcpRuntime,\n SessionMcpRuntimeManager,\n} from \"./bundle-mcp-types.js\";\n\ntype BundleMcpSession = {\n serverName: string;\n client: Client;\n transport: Transport;\n transportType: \"stdio\" | \"sse\" | \"streamable-http\";\n detachStderr?: () => void;\n};\n\ntype LoadedMcpConfig = ReturnType<typeof loadEmbeddedMcpConfig>;\ntype ListedTool = Awaited<ReturnType<Client[\"listTools\"]>>[\"tools\"][number];\ntype CreateSessionMcpRuntime = (\n params: Parameters<typeof createSessionMcpRuntime>[0] & { configFingerprint?: string },\n) => SessionMcpRuntime;\n\nconst SESSION_MCP_RUNTIME_MANAGER_KEY = Symbol.for(\"xopc.sessionMcpRuntimeManager\");\nconst DRAFT_2020_12_SCHEMA = \"https://json-schema.org/draft/2020-12/schema\";\nconst DEFAULT_SESSION_MCP_RUNTIME_IDLE_TTL_MS = 10 * 60 * 1000;\nconst SESSION_MCP_RUNTIME_SWEEP_INTERVAL_MS = 60 * 1000;\n\nfunction isDraft202012Schema(schema: JsonSchemaType): boolean {\n return (schema as { $schema?: unknown }).$schema === DRAFT_2020_12_SCHEMA;\n}\n\nexport function createBundleMcpJsonSchemaValidator(): jsonSchemaValidator {\n const defaultValidator = new AjvJsonSchemaValidator();\n const ajv2020 = new Ajv2020({\n strict: false,\n validateFormats: false,\n validateSchema: false,\n allErrors: true,\n });\n\n return {\n getValidator<T>(schema: JsonSchemaType): JsonSchemaValidator<T> {\n if (!isDraft202012Schema(schema)) {\n return defaultValidator.getValidator<T>(schema);\n }\n const ajvValidator = ajv2020.compile(schema);\n return (input: unknown) => {\n const valid = ajvValidator(input);\n if (valid) {\n return {\n valid: true,\n data: input as T,\n errorMessage: undefined,\n };\n }\n return {\n valid: false,\n data: undefined,\n errorMessage: ajv2020.errorsText(ajvValidator.errors),\n };\n };\n },\n };\n}\n\nfunction connectWithTimeout(\n client: Client,\n transport: Transport,\n timeoutMs: number,\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const timer = setTimeout(\n () => reject(new Error(`MCP server connection timed out after ${timeoutMs}ms`)),\n timeoutMs,\n );\n client.connect(transport).then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (error) => {\n clearTimeout(timer);\n reject(error);\n },\n );\n });\n}\n\nfunction redactErrorUrls(error: unknown): string {\n return redactSensitiveUrlLikeString(String(error));\n}\n\nasync function listAllTools(client: Client) {\n const tools: ListedTool[] = [];\n let cursor: string | undefined;\n do {\n const page = await client.listTools(cursor ? { cursor } : undefined);\n tools.push(...page.tools);\n cursor = page.nextCursor;\n } while (cursor);\n return tools;\n}\n\nasync function disposeSession(session: BundleMcpSession) {\n session.detachStderr?.();\n if (session.transportType === \"streamable-http\") {\n await (session.transport as StreamableHTTPClientTransport).terminateSession().catch(() => {});\n }\n await session.transport.close().catch(() => {});\n await session.client.close().catch(() => {});\n}\n\nfunction createCatalogFingerprint(servers: Record<string, unknown>): string {\n return crypto.createHash(\"sha1\").update(JSON.stringify(servers)).digest(\"hex\");\n}\n\nfunction loadSessionMcpConfig(params: {\n workspaceDir: string;\n cfg?: Config;\n logDiagnostics?: boolean;\n}): {\n loaded: LoadedMcpConfig;\n fingerprint: string;\n} {\n const loaded = loadEmbeddedMcpConfig({\n workspaceDir: params.workspaceDir,\n cfg: params.cfg,\n });\n if (params.logDiagnostics !== false) {\n for (const diagnostic of loaded.diagnostics) {\n log.warn(\n {\n phase: 'agent.mcp_connect',\n extensionId: diagnostic.extensionId,\n message: diagnostic.message,\n },\n `bundle-mcp diagnostic: ${diagnostic.extensionId}: ${diagnostic.message}`,\n );\n }\n }\n return {\n loaded,\n fingerprint: createCatalogFingerprint(loaded.mcpServers),\n };\n}\n\nfunction createDisposedError(sessionId: string): Error {\n return new Error(`bundle-mcp runtime disposed for session ${sessionId}`);\n}\n\nfunction resolveSessionMcpRuntimeIdleTtlMs(cfg?: Config): number {\n const raw = cfg?.mcp?.sessionIdleTtlMs;\n if (typeof raw === \"number\" && Number.isFinite(raw) && raw >= 0) {\n return Math.floor(raw);\n }\n return DEFAULT_SESSION_MCP_RUNTIME_IDLE_TTL_MS;\n}\n\nexport function createSessionMcpRuntime(params: {\n sessionId: string;\n sessionKey?: string;\n workspaceDir: string;\n cfg?: Config;\n}): SessionMcpRuntime {\n const { loaded, fingerprint: configFingerprint } = loadSessionMcpConfig({\n workspaceDir: params.workspaceDir,\n cfg: params.cfg,\n logDiagnostics: true,\n });\n const createdAt = Date.now();\n let lastUsedAt = createdAt;\n let activeLeases = 0;\n let disposed = false;\n let catalog: McpToolCatalog | null = null;\n let catalogInFlight: Promise<McpToolCatalog> | undefined;\n const sessions = new Map<string, BundleMcpSession>();\n const failIfDisposed = () => {\n if (disposed) {\n throw createDisposedError(params.sessionId);\n }\n };\n\n const getCatalog = async (): Promise<McpToolCatalog> => {\n failIfDisposed();\n if (catalog) {\n return catalog;\n }\n if (catalogInFlight) {\n return catalogInFlight;\n }\n catalogInFlight = (async () => {\n if (Object.keys(loaded.mcpServers).length === 0) {\n return {\n version: 1,\n generatedAt: Date.now(),\n servers: {},\n tools: [],\n };\n }\n\n const servers: Record<string, McpServerCatalog> = {};\n const tools: McpCatalogTool[] = [];\n const usedServerNames = new Set<string>();\n\n try {\n for (const [serverName, rawServer] of Object.entries(loaded.mcpServers)) {\n failIfDisposed();\n const resolvedServer = await resolveConnectorSecretReferences(rawServer);\n const resolved = resolveMcpTransport(serverName, resolvedServer);\n if (!resolved) {\n continue;\n }\n const safeServerName = sanitizeServerName(serverName, usedServerNames);\n if (safeServerName !== serverName) {\n log.warn(\n {\n phase: 'agent.mcp_connect',\n serverName,\n safeServerName,\n },\n `bundle-mcp: server key \"${serverName}\" registered as \"${safeServerName}\" for provider-safe tool names`,\n );\n }\n\n const client = new Client(\n {\n name: \"xopc-bundle-mcp\",\n version: \"0.0.0\",\n },\n {\n jsonSchemaValidator: createBundleMcpJsonSchemaValidator(),\n },\n );\n const session: BundleMcpSession = {\n serverName,\n client,\n transport: resolved.transport,\n transportType: resolved.transportType,\n detachStderr: resolved.detachStderr,\n };\n sessions.set(serverName, session);\n\n try {\n failIfDisposed();\n await connectWithTimeout(client, resolved.transport, resolved.connectionTimeoutMs);\n failIfDisposed();\n const listedTools = await listAllTools(client);\n failIfDisposed();\n servers[serverName] = {\n serverName,\n launchSummary: resolved.description,\n toolCount: listedTools.length,\n };\n for (const tool of listedTools) {\n const toolName = tool.name.trim();\n if (!toolName) {\n continue;\n }\n tools.push({\n serverName,\n safeServerName,\n toolName,\n title: tool.title,\n description:\n normalizeOptionalString(tool.description) ?? normalizeOptionalString(tool.title),\n inputSchema: tool.inputSchema,\n fallbackDescription: `Provided by bundle MCP server \"${serverName}\" (${resolved.description}).`,\n });\n }\n } catch (error) {\n if (!disposed) {\n log.warn(\n `bundle-mcp: failed to start server \"${serverName}\" (${resolved.description}): ${redactErrorUrls(error)}`,\n );\n }\n await disposeSession(session);\n sessions.delete(serverName);\n failIfDisposed();\n }\n }\n\n failIfDisposed();\n return {\n version: 1,\n generatedAt: Date.now(),\n servers,\n tools,\n };\n } catch (error) {\n await Promise.allSettled(\n Array.from(sessions.values(), (session) => disposeSession(session)),\n );\n sessions.clear();\n throw error;\n }\n })();\n\n try {\n const nextCatalog = await catalogInFlight;\n failIfDisposed();\n catalog = nextCatalog;\n return nextCatalog;\n } finally {\n catalogInFlight = undefined;\n }\n };\n\n return {\n sessionId: params.sessionId,\n sessionKey: params.sessionKey,\n workspaceDir: params.workspaceDir,\n configFingerprint,\n createdAt,\n get lastUsedAt() {\n return lastUsedAt;\n },\n get activeLeases() {\n return activeLeases;\n },\n acquireLease() {\n activeLeases += 1;\n let released = false;\n return () => {\n if (released) {\n return;\n }\n released = true;\n activeLeases = Math.max(0, activeLeases - 1);\n lastUsedAt = Date.now();\n };\n },\n getCatalog,\n markUsed() {\n lastUsedAt = Date.now();\n },\n async callTool(serverName, toolName, input) {\n failIfDisposed();\n await getCatalog();\n const session = sessions.get(serverName);\n if (!session) {\n throw new Error(`bundle-mcp server \"${serverName}\" is not connected`);\n }\n return (await session.client.callTool({\n name: toolName,\n arguments: isMcpConfigRecord(input) ? input : {},\n })) as CallToolResult;\n },\n async dispose() {\n if (disposed) {\n return;\n }\n disposed = true;\n catalog = null;\n catalogInFlight = undefined;\n const sessionsToClose = Array.from(sessions.values());\n sessions.clear();\n await Promise.allSettled(sessionsToClose.map((session) => disposeSession(session)));\n },\n };\n}\n\nfunction createSessionMcpRuntimeManager(\n opts: {\n createRuntime?: CreateSessionMcpRuntime;\n now?: () => number;\n enableIdleSweepTimer?: boolean;\n idleSweepIntervalMs?: number;\n } = {},\n): SessionMcpRuntimeManager {\n const runtimesBySessionId = new Map<string, SessionMcpRuntime>();\n const sessionIdBySessionKey = new Map<string, string>();\n const idleTtlMsBySessionId = new Map<string, number>();\n const createRuntime = opts.createRuntime ?? createSessionMcpRuntime;\n const now = opts.now ?? Date.now;\n const createInFlight = new Map<\n string,\n {\n promise: Promise<SessionMcpRuntime>;\n workspaceDir: string;\n configFingerprint: string;\n }\n >();\n const idleSweepIntervalMs = opts.idleSweepIntervalMs ?? SESSION_MCP_RUNTIME_SWEEP_INTERVAL_MS;\n let idleSweepTimer: ReturnType<typeof setInterval> | undefined;\n let idleSweepInFlight: Promise<void> | undefined;\n\n const forgetSessionKeysForSessionId = (sessionId: string) => {\n for (const [sessionKey, mappedSessionId] of sessionIdBySessionKey.entries()) {\n if (mappedSessionId === sessionId) {\n sessionIdBySessionKey.delete(sessionKey);\n }\n }\n };\n\n const sweepIdleRuntimes = async (): Promise<number> => {\n const nowMs = now();\n const expired: SessionMcpRuntime[] = [];\n for (const [sessionId, runtime] of runtimesBySessionId.entries()) {\n const idleTtlMs =\n idleTtlMsBySessionId.get(sessionId) ?? DEFAULT_SESSION_MCP_RUNTIME_IDLE_TTL_MS;\n if (idleTtlMs <= 0 || (runtime.activeLeases ?? 0) > 0) {\n continue;\n }\n if (nowMs - runtime.lastUsedAt < idleTtlMs) {\n continue;\n }\n runtimesBySessionId.delete(sessionId);\n idleTtlMsBySessionId.delete(sessionId);\n forgetSessionKeysForSessionId(sessionId);\n expired.push(runtime);\n }\n await Promise.allSettled(expired.map((runtime) => runtime.dispose()));\n return expired.length;\n };\n\n const queueIdleSweep = () => {\n if (idleSweepInFlight) {\n return;\n }\n idleSweepInFlight = sweepIdleRuntimes()\n .then(() => undefined)\n .catch((error: unknown) => {\n const em = error instanceof Error ? error.message : String(error);\n log.warn(\n { err: error, errorMessage: em, phase: 'agent.mcp_connect' },\n `bundle-mcp: idle runtime sweep failed: ${em}`,\n );\n })\n .finally(() => {\n idleSweepInFlight = undefined;\n });\n };\n\n const ensureIdleSweepTimer = () => {\n if (opts.enableIdleSweepTimer === false || idleSweepIntervalMs <= 0 || idleSweepTimer) {\n return;\n }\n idleSweepTimer = setInterval(queueIdleSweep, idleSweepIntervalMs);\n idleSweepTimer.unref?.();\n };\n\n const clearIdleSweepTimer = () => {\n if (!idleSweepTimer) {\n return;\n }\n clearInterval(idleSweepTimer);\n idleSweepTimer = undefined;\n };\n\n return {\n async getOrCreate(params) {\n const idleTtlMs = resolveSessionMcpRuntimeIdleTtlMs(params.cfg);\n if (runtimesBySessionId.has(params.sessionId)) {\n idleTtlMsBySessionId.set(params.sessionId, idleTtlMs);\n }\n await sweepIdleRuntimes();\n if (idleTtlMs > 0) {\n ensureIdleSweepTimer();\n }\n if (params.sessionKey) {\n sessionIdBySessionKey.set(params.sessionKey, params.sessionId);\n }\n const { fingerprint: nextFingerprint } = loadSessionMcpConfig({\n workspaceDir: params.workspaceDir,\n cfg: params.cfg,\n logDiagnostics: false,\n });\n const existing = runtimesBySessionId.get(params.sessionId);\n if (existing) {\n if (\n existing.workspaceDir !== params.workspaceDir ||\n existing.configFingerprint !== nextFingerprint\n ) {\n runtimesBySessionId.delete(params.sessionId);\n await existing.dispose();\n } else {\n existing.markUsed();\n idleTtlMsBySessionId.set(params.sessionId, idleTtlMs);\n return existing;\n }\n }\n const inFlight = createInFlight.get(params.sessionId);\n if (inFlight) {\n if (\n inFlight.workspaceDir === params.workspaceDir &&\n inFlight.configFingerprint === nextFingerprint\n ) {\n return inFlight.promise;\n }\n createInFlight.delete(params.sessionId);\n const staleRuntime = await inFlight.promise.catch(() => undefined);\n runtimesBySessionId.delete(params.sessionId);\n idleTtlMsBySessionId.delete(params.sessionId);\n await staleRuntime?.dispose();\n }\n const created = Promise.resolve(\n createRuntime({\n sessionId: params.sessionId,\n sessionKey: params.sessionKey,\n workspaceDir: params.workspaceDir,\n cfg: params.cfg,\n }),\n ).then((runtime) => {\n runtime.markUsed();\n runtimesBySessionId.set(params.sessionId, runtime);\n idleTtlMsBySessionId.set(params.sessionId, idleTtlMs);\n return runtime;\n });\n createInFlight.set(params.sessionId, {\n promise: created,\n workspaceDir: params.workspaceDir,\n configFingerprint: nextFingerprint,\n });\n try {\n return await created;\n } finally {\n createInFlight.delete(params.sessionId);\n }\n },\n bindSessionKey(sessionKey, sessionId) {\n sessionIdBySessionKey.set(sessionKey, sessionId);\n },\n resolveSessionId(sessionKey) {\n return sessionIdBySessionKey.get(sessionKey);\n },\n async disposeSession(sessionId) {\n const inFlight = createInFlight.get(sessionId);\n createInFlight.delete(sessionId);\n let runtime = runtimesBySessionId.get(sessionId);\n if (!runtime && inFlight) {\n runtime = await inFlight.promise.catch(() => undefined);\n }\n runtimesBySessionId.delete(sessionId);\n idleTtlMsBySessionId.delete(sessionId);\n if (!runtime) {\n forgetSessionKeysForSessionId(sessionId);\n return;\n }\n forgetSessionKeysForSessionId(sessionId);\n await runtime.dispose();\n },\n async disposeAll() {\n clearIdleSweepTimer();\n const inFlightRuntimes = Array.from(createInFlight.values());\n createInFlight.clear();\n const runtimes = Array.from(runtimesBySessionId.values());\n runtimesBySessionId.clear();\n sessionIdBySessionKey.clear();\n idleTtlMsBySessionId.clear();\n const lateRuntimes = await Promise.all(\n inFlightRuntimes.map(async ({ promise }) => await promise.catch(() => undefined)),\n );\n const allRuntimes = new Set<SessionMcpRuntime>(runtimes);\n for (const runtime of lateRuntimes) {\n if (runtime) {\n allRuntimes.add(runtime);\n }\n }\n await Promise.allSettled(Array.from(allRuntimes, (runtime) => runtime.dispose()));\n },\n sweepIdleRuntimes,\n listSessionIds() {\n return Array.from(runtimesBySessionId.keys());\n },\n };\n}\n\nexport function getSessionMcpRuntimeManager(): SessionMcpRuntimeManager {\n return resolveGlobalSingleton(SESSION_MCP_RUNTIME_MANAGER_KEY, createSessionMcpRuntimeManager);\n}\n\nexport async function getOrCreateSessionMcpRuntime(params: {\n sessionId: string;\n sessionKey?: string;\n workspaceDir: string;\n cfg?: Config;\n}): Promise<SessionMcpRuntime> {\n return await getSessionMcpRuntimeManager().getOrCreate(params);\n}\n\nexport async function disposeSessionMcpRuntime(sessionId: string): Promise<void> {\n await getSessionMcpRuntimeManager().disposeSession(sessionId);\n}\n\nexport async function retireSessionMcpRuntime(params: {\n sessionId?: string | null;\n reason: string;\n onError?: (error: unknown, sessionId: string, reason: string) => void;\n}): Promise<boolean> {\n const sessionId = normalizeOptionalString(params.sessionId);\n if (!sessionId) {\n return false;\n }\n try {\n await disposeSessionMcpRuntime(sessionId);\n return true;\n } catch (error) {\n params.onError?.(error, sessionId, params.reason);\n return false;\n }\n}\n\nexport async function retireSessionMcpRuntimeForSessionKey(params: {\n sessionKey?: string | null;\n reason: string;\n onError?: (error: unknown, sessionId: string, reason: string) => void;\n}): Promise<boolean> {\n const sessionKey = normalizeOptionalString(params.sessionKey);\n if (!sessionKey) {\n return false;\n }\n const sessionId = getSessionMcpRuntimeManager().resolveSessionId(sessionKey);\n return await retireSessionMcpRuntime({\n sessionId,\n reason: params.reason,\n onError: params.onError,\n });\n}\n\nexport async function disposeAllSessionMcpRuntimes(): Promise<void> {\n await getSessionMcpRuntimeManager().disposeAll();\n}\n\nexport const __testing = {\n createSessionMcpRuntimeManager,\n async resetSessionMcpRuntimeManager() {\n await disposeAllSessionMcpRuntimes();\n },\n getCachedSessionIds() {\n return getSessionMcpRuntimeManager().listSessionIds();\n },\n resolveSessionMcpRuntimeIdleTtlMs,\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"bundle-mcp-runtime.js","names":[],"sources":["../../../../src/agent/mcp/bundle-mcp-runtime.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport Ajv2020 from \"ajv/dist/2020.js\";\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { AjvJsonSchemaValidator } from \"@modelcontextprotocol/sdk/validation/ajv-provider.js\";\nimport type {\n JsonSchemaType,\n JsonSchemaValidator,\n jsonSchemaValidator,\n} from \"@modelcontextprotocol/sdk/validation/types.js\";\nimport type { Config } from \"../../config/schema.js\";\nimport { createLogger } from \"../../utils/logger.js\";\nconst log = createLogger(\"Mcp:Bundle\");\nimport { resolveGlobalSingleton } from \"../../utils/global-singleton.js\";\nimport { redactSensitiveUrlLikeString } from \"../../utils/redact-sensitive-url.js\";\nimport { normalizeOptionalString } from \"../../utils/string-coerce.js\";\nimport { loadEmbeddedMcpConfig } from \"./embedded-mcp.js\";\nimport { resolveConnectorSecretReferences } from \"../../connectors/secret-store.js\";\nimport { isMcpConfigRecord } from \"./mcp-config-shared.js\";\nimport { resolveMcpTransport } from \"./mcp-transport.js\";\nimport { sanitizeServerName } from \"./bundle-mcp-names.js\";\nimport type {\n McpCatalogTool,\n McpServerCatalog,\n McpToolCatalog,\n SessionMcpRuntime,\n SessionMcpRuntimeManager,\n} from \"./bundle-mcp-types.js\";\n\ntype BundleMcpSession = {\n serverName: string;\n client: Client;\n transport: Transport;\n transportType: \"stdio\" | \"sse\" | \"streamable-http\";\n detachStderr?: () => void;\n};\n\ntype LoadedMcpConfig = ReturnType<typeof loadEmbeddedMcpConfig>;\ntype ListedTool = Awaited<ReturnType<Client[\"listTools\"]>>[\"tools\"][number];\ntype ListedResource = Awaited<ReturnType<Client[\"listResources\"]>>[\"resources\"][number];\ntype ListedPrompt = Awaited<ReturnType<Client[\"listPrompts\"]>>[\"prompts\"][number];\ntype CreateSessionMcpRuntime = (\n params: Parameters<typeof createSessionMcpRuntime>[0] & { configFingerprint?: string },\n) => SessionMcpRuntime;\n\nconst SESSION_MCP_RUNTIME_MANAGER_KEY = Symbol.for(\"xopc.sessionMcpRuntimeManager\");\nconst DRAFT_2020_12_SCHEMA = \"https://json-schema.org/draft/2020-12/schema\";\nconst DEFAULT_SESSION_MCP_RUNTIME_IDLE_TTL_MS = 10 * 60 * 1000;\nconst SESSION_MCP_RUNTIME_SWEEP_INTERVAL_MS = 60 * 1000;\n\nfunction isDraft202012Schema(schema: JsonSchemaType): boolean {\n return (schema as { $schema?: unknown }).$schema === DRAFT_2020_12_SCHEMA;\n}\n\nexport function createBundleMcpJsonSchemaValidator(): jsonSchemaValidator {\n const defaultValidator = new AjvJsonSchemaValidator();\n const ajv2020 = new Ajv2020({\n strict: false,\n validateFormats: false,\n validateSchema: false,\n allErrors: true,\n });\n\n return {\n getValidator<T>(schema: JsonSchemaType): JsonSchemaValidator<T> {\n if (!isDraft202012Schema(schema)) {\n return defaultValidator.getValidator<T>(schema);\n }\n const ajvValidator = ajv2020.compile(schema);\n return (input: unknown) => {\n const valid = ajvValidator(input);\n if (valid) {\n return {\n valid: true,\n data: input as T,\n errorMessage: undefined,\n };\n }\n return {\n valid: false,\n data: undefined,\n errorMessage: ajv2020.errorsText(ajvValidator.errors),\n };\n };\n },\n };\n}\n\nfunction connectWithTimeout(\n client: Client,\n transport: Transport,\n timeoutMs: number,\n): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const timer = setTimeout(\n () => reject(new Error(`MCP server connection timed out after ${timeoutMs}ms`)),\n timeoutMs,\n );\n client.connect(transport).then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (error) => {\n clearTimeout(timer);\n reject(error);\n },\n );\n });\n}\n\nfunction redactErrorUrls(error: unknown): string {\n return redactSensitiveUrlLikeString(String(error));\n}\n\nasync function listAllTools(client: Client) {\n const tools: ListedTool[] = [];\n let cursor: string | undefined;\n do {\n const page = await client.listTools(cursor ? { cursor } : undefined);\n tools.push(...page.tools);\n cursor = page.nextCursor;\n } while (cursor);\n return tools;\n}\n\nasync function listAllResources(client: Client) {\n const resources: ListedResource[] = [];\n let cursor: string | undefined;\n do {\n const page = await client.listResources(cursor ? { cursor } : undefined);\n resources.push(...page.resources);\n cursor = page.nextCursor;\n } while (cursor);\n return resources;\n}\n\nasync function listAllPrompts(client: Client) {\n const prompts: ListedPrompt[] = [];\n let cursor: string | undefined;\n do {\n const page = await client.listPrompts(cursor ? { cursor } : undefined);\n prompts.push(...page.prompts);\n cursor = page.nextCursor;\n } while (cursor);\n return prompts;\n}\n\nasync function listOptionalMcpCapability<T>(params: {\n serverName: string;\n capability: \"resources\" | \"prompts\";\n list: () => Promise<T[]>;\n}): Promise<T[]> {\n try {\n return await params.list();\n } catch (error) {\n log.debug(\n {\n phase: \"agent.mcp_connect\",\n serverName: params.serverName,\n capability: params.capability,\n errorMessage: redactErrorUrls(error),\n },\n `bundle-mcp: server \"${params.serverName}\" did not list ${params.capability}`,\n );\n return [];\n }\n}\n\nasync function disposeSession(session: BundleMcpSession) {\n session.detachStderr?.();\n if (session.transportType === \"streamable-http\") {\n await (session.transport as StreamableHTTPClientTransport).terminateSession().catch(() => {});\n }\n await session.transport.close().catch(() => {});\n await session.client.close().catch(() => {});\n}\n\nfunction createCatalogFingerprint(servers: Record<string, unknown>): string {\n return crypto.createHash(\"sha1\").update(JSON.stringify(servers)).digest(\"hex\");\n}\n\nfunction loadSessionMcpConfig(params: {\n workspaceDir: string;\n cfg?: Config;\n logDiagnostics?: boolean;\n}): {\n loaded: LoadedMcpConfig;\n fingerprint: string;\n} {\n const loaded = loadEmbeddedMcpConfig({\n workspaceDir: params.workspaceDir,\n cfg: params.cfg,\n });\n if (params.logDiagnostics !== false) {\n for (const diagnostic of loaded.diagnostics) {\n log.warn(\n {\n phase: 'agent.mcp_connect',\n extensionId: diagnostic.extensionId,\n message: diagnostic.message,\n },\n `bundle-mcp diagnostic: ${diagnostic.extensionId}: ${diagnostic.message}`,\n );\n }\n }\n return {\n loaded,\n fingerprint: createCatalogFingerprint(loaded.mcpServers),\n };\n}\n\nfunction createDisposedError(sessionId: string): Error {\n return new Error(`bundle-mcp runtime disposed for session ${sessionId}`);\n}\n\nfunction resolveSessionMcpRuntimeIdleTtlMs(cfg?: Config): number {\n const raw = cfg?.mcp?.sessionIdleTtlMs;\n if (typeof raw === \"number\" && Number.isFinite(raw) && raw >= 0) {\n return Math.floor(raw);\n }\n return DEFAULT_SESSION_MCP_RUNTIME_IDLE_TTL_MS;\n}\n\nexport function createSessionMcpRuntime(params: {\n sessionId: string;\n sessionKey?: string;\n workspaceDir: string;\n cfg?: Config;\n}): SessionMcpRuntime {\n const { loaded, fingerprint: configFingerprint } = loadSessionMcpConfig({\n workspaceDir: params.workspaceDir,\n cfg: params.cfg,\n logDiagnostics: true,\n });\n const createdAt = Date.now();\n let lastUsedAt = createdAt;\n let activeLeases = 0;\n let disposed = false;\n let catalog: McpToolCatalog | null = null;\n let catalogInFlight: Promise<McpToolCatalog> | undefined;\n const sessions = new Map<string, BundleMcpSession>();\n const failIfDisposed = () => {\n if (disposed) {\n throw createDisposedError(params.sessionId);\n }\n };\n\n const getCatalog = async (): Promise<McpToolCatalog> => {\n failIfDisposed();\n if (catalog) {\n return catalog;\n }\n if (catalogInFlight) {\n return catalogInFlight;\n }\n catalogInFlight = (async () => {\n if (Object.keys(loaded.mcpServers).length === 0) {\n return {\n version: 1,\n generatedAt: Date.now(),\n servers: {},\n tools: [],\n resources: [],\n prompts: [],\n };\n }\n\n const servers: Record<string, McpServerCatalog> = {};\n const tools: McpCatalogTool[] = [];\n const resources: McpToolCatalog[\"resources\"] = [];\n const prompts: McpToolCatalog[\"prompts\"] = [];\n const usedServerNames = new Set<string>();\n\n try {\n for (const [serverName, rawServer] of Object.entries(loaded.mcpServers)) {\n failIfDisposed();\n const resolvedServer = await resolveConnectorSecretReferences(rawServer);\n const resolved = resolveMcpTransport(serverName, resolvedServer);\n if (!resolved) {\n continue;\n }\n const safeServerName = sanitizeServerName(serverName, usedServerNames);\n if (safeServerName !== serverName) {\n log.warn(\n {\n phase: 'agent.mcp_connect',\n serverName,\n safeServerName,\n },\n `bundle-mcp: server key \"${serverName}\" registered as \"${safeServerName}\" for provider-safe tool names`,\n );\n }\n\n const client = new Client(\n {\n name: \"xopc-bundle-mcp\",\n version: \"0.0.0\",\n },\n {\n jsonSchemaValidator: createBundleMcpJsonSchemaValidator(),\n },\n );\n const session: BundleMcpSession = {\n serverName,\n client,\n transport: resolved.transport,\n transportType: resolved.transportType,\n detachStderr: resolved.detachStderr,\n };\n sessions.set(serverName, session);\n\n try {\n failIfDisposed();\n await connectWithTimeout(client, resolved.transport, resolved.connectionTimeoutMs);\n failIfDisposed();\n const listedTools = await listAllTools(client);\n const listedResources = await listOptionalMcpCapability({\n serverName,\n capability: \"resources\",\n list: () => listAllResources(client),\n });\n const listedPrompts = await listOptionalMcpCapability({\n serverName,\n capability: \"prompts\",\n list: () => listAllPrompts(client),\n });\n failIfDisposed();\n servers[serverName] = {\n serverName,\n launchSummary: resolved.description,\n toolCount: listedTools.length,\n resourceCount: listedResources.length,\n promptCount: listedPrompts.length,\n };\n for (const tool of listedTools) {\n const toolName = tool.name.trim();\n if (!toolName) {\n continue;\n }\n tools.push({\n serverName,\n safeServerName,\n toolName,\n title: tool.title,\n description:\n normalizeOptionalString(tool.description) ?? normalizeOptionalString(tool.title),\n inputSchema: tool.inputSchema,\n fallbackDescription: `Provided by bundle MCP server \"${serverName}\" (${resolved.description}).`,\n });\n }\n for (const resource of listedResources) {\n const uri = resource.uri.trim();\n const name = resource.name.trim();\n if (!uri || !name) {\n continue;\n }\n resources.push({\n serverName,\n uri,\n name,\n title: resource.title,\n description: normalizeOptionalString(resource.description),\n mimeType: normalizeOptionalString(resource.mimeType),\n });\n }\n for (const prompt of listedPrompts) {\n const name = prompt.name.trim();\n if (!name) {\n continue;\n }\n prompts.push({\n serverName,\n name,\n title: prompt.title,\n description: normalizeOptionalString(prompt.description),\n argumentCount: Array.isArray(prompt.arguments) ? prompt.arguments.length : 0,\n });\n }\n } catch (error) {\n if (!disposed) {\n log.warn(\n `bundle-mcp: failed to start server \"${serverName}\" (${resolved.description}): ${redactErrorUrls(error)}`,\n );\n }\n await disposeSession(session);\n sessions.delete(serverName);\n failIfDisposed();\n }\n }\n\n failIfDisposed();\n return {\n version: 1,\n generatedAt: Date.now(),\n servers,\n tools,\n resources,\n prompts,\n };\n } catch (error) {\n await Promise.allSettled(\n Array.from(sessions.values(), (session) => disposeSession(session)),\n );\n sessions.clear();\n throw error;\n }\n })();\n\n try {\n const nextCatalog = await catalogInFlight;\n failIfDisposed();\n catalog = nextCatalog;\n return nextCatalog;\n } finally {\n catalogInFlight = undefined;\n }\n };\n\n return {\n sessionId: params.sessionId,\n sessionKey: params.sessionKey,\n workspaceDir: params.workspaceDir,\n configFingerprint,\n createdAt,\n get lastUsedAt() {\n return lastUsedAt;\n },\n get activeLeases() {\n return activeLeases;\n },\n acquireLease() {\n activeLeases += 1;\n let released = false;\n return () => {\n if (released) {\n return;\n }\n released = true;\n activeLeases = Math.max(0, activeLeases - 1);\n lastUsedAt = Date.now();\n };\n },\n getCatalog,\n markUsed() {\n lastUsedAt = Date.now();\n },\n async callTool(serverName, toolName, input) {\n failIfDisposed();\n await getCatalog();\n const session = sessions.get(serverName);\n if (!session) {\n throw new Error(`bundle-mcp server \"${serverName}\" is not connected`);\n }\n return (await session.client.callTool({\n name: toolName,\n arguments: isMcpConfigRecord(input) ? input : {},\n })) as CallToolResult;\n },\n async dispose() {\n if (disposed) {\n return;\n }\n disposed = true;\n catalog = null;\n catalogInFlight = undefined;\n const sessionsToClose = Array.from(sessions.values());\n sessions.clear();\n await Promise.allSettled(sessionsToClose.map((session) => disposeSession(session)));\n },\n };\n}\n\nfunction createSessionMcpRuntimeManager(\n opts: {\n createRuntime?: CreateSessionMcpRuntime;\n now?: () => number;\n enableIdleSweepTimer?: boolean;\n idleSweepIntervalMs?: number;\n } = {},\n): SessionMcpRuntimeManager {\n const runtimesBySessionId = new Map<string, SessionMcpRuntime>();\n const sessionIdBySessionKey = new Map<string, string>();\n const idleTtlMsBySessionId = new Map<string, number>();\n const createRuntime = opts.createRuntime ?? createSessionMcpRuntime;\n const now = opts.now ?? Date.now;\n const createInFlight = new Map<\n string,\n {\n promise: Promise<SessionMcpRuntime>;\n workspaceDir: string;\n configFingerprint: string;\n }\n >();\n const idleSweepIntervalMs = opts.idleSweepIntervalMs ?? SESSION_MCP_RUNTIME_SWEEP_INTERVAL_MS;\n let idleSweepTimer: ReturnType<typeof setInterval> | undefined;\n let idleSweepInFlight: Promise<void> | undefined;\n\n const forgetSessionKeysForSessionId = (sessionId: string) => {\n for (const [sessionKey, mappedSessionId] of sessionIdBySessionKey.entries()) {\n if (mappedSessionId === sessionId) {\n sessionIdBySessionKey.delete(sessionKey);\n }\n }\n };\n\n const sweepIdleRuntimes = async (): Promise<number> => {\n const nowMs = now();\n const expired: SessionMcpRuntime[] = [];\n for (const [sessionId, runtime] of runtimesBySessionId.entries()) {\n const idleTtlMs =\n idleTtlMsBySessionId.get(sessionId) ?? DEFAULT_SESSION_MCP_RUNTIME_IDLE_TTL_MS;\n if (idleTtlMs <= 0 || (runtime.activeLeases ?? 0) > 0) {\n continue;\n }\n if (nowMs - runtime.lastUsedAt < idleTtlMs) {\n continue;\n }\n runtimesBySessionId.delete(sessionId);\n idleTtlMsBySessionId.delete(sessionId);\n forgetSessionKeysForSessionId(sessionId);\n expired.push(runtime);\n }\n await Promise.allSettled(expired.map((runtime) => runtime.dispose()));\n return expired.length;\n };\n\n const queueIdleSweep = () => {\n if (idleSweepInFlight) {\n return;\n }\n idleSweepInFlight = sweepIdleRuntimes()\n .then(() => undefined)\n .catch((error: unknown) => {\n const em = error instanceof Error ? error.message : String(error);\n log.warn(\n { err: error, errorMessage: em, phase: 'agent.mcp_connect' },\n `bundle-mcp: idle runtime sweep failed: ${em}`,\n );\n })\n .finally(() => {\n idleSweepInFlight = undefined;\n });\n };\n\n const ensureIdleSweepTimer = () => {\n if (opts.enableIdleSweepTimer === false || idleSweepIntervalMs <= 0 || idleSweepTimer) {\n return;\n }\n idleSweepTimer = setInterval(queueIdleSweep, idleSweepIntervalMs);\n idleSweepTimer.unref?.();\n };\n\n const clearIdleSweepTimer = () => {\n if (!idleSweepTimer) {\n return;\n }\n clearInterval(idleSweepTimer);\n idleSweepTimer = undefined;\n };\n\n return {\n async getOrCreate(params) {\n const idleTtlMs = resolveSessionMcpRuntimeIdleTtlMs(params.cfg);\n if (runtimesBySessionId.has(params.sessionId)) {\n idleTtlMsBySessionId.set(params.sessionId, idleTtlMs);\n }\n await sweepIdleRuntimes();\n if (idleTtlMs > 0) {\n ensureIdleSweepTimer();\n }\n if (params.sessionKey) {\n sessionIdBySessionKey.set(params.sessionKey, params.sessionId);\n }\n const { fingerprint: nextFingerprint } = loadSessionMcpConfig({\n workspaceDir: params.workspaceDir,\n cfg: params.cfg,\n logDiagnostics: false,\n });\n const existing = runtimesBySessionId.get(params.sessionId);\n if (existing) {\n if (\n existing.workspaceDir !== params.workspaceDir ||\n existing.configFingerprint !== nextFingerprint\n ) {\n runtimesBySessionId.delete(params.sessionId);\n await existing.dispose();\n } else {\n existing.markUsed();\n idleTtlMsBySessionId.set(params.sessionId, idleTtlMs);\n return existing;\n }\n }\n const inFlight = createInFlight.get(params.sessionId);\n if (inFlight) {\n if (\n inFlight.workspaceDir === params.workspaceDir &&\n inFlight.configFingerprint === nextFingerprint\n ) {\n return inFlight.promise;\n }\n createInFlight.delete(params.sessionId);\n const staleRuntime = await inFlight.promise.catch(() => undefined);\n runtimesBySessionId.delete(params.sessionId);\n idleTtlMsBySessionId.delete(params.sessionId);\n await staleRuntime?.dispose();\n }\n const created = Promise.resolve(\n createRuntime({\n sessionId: params.sessionId,\n sessionKey: params.sessionKey,\n workspaceDir: params.workspaceDir,\n cfg: params.cfg,\n }),\n ).then((runtime) => {\n runtime.markUsed();\n runtimesBySessionId.set(params.sessionId, runtime);\n idleTtlMsBySessionId.set(params.sessionId, idleTtlMs);\n return runtime;\n });\n createInFlight.set(params.sessionId, {\n promise: created,\n workspaceDir: params.workspaceDir,\n configFingerprint: nextFingerprint,\n });\n try {\n return await created;\n } finally {\n createInFlight.delete(params.sessionId);\n }\n },\n bindSessionKey(sessionKey, sessionId) {\n sessionIdBySessionKey.set(sessionKey, sessionId);\n },\n resolveSessionId(sessionKey) {\n return sessionIdBySessionKey.get(sessionKey);\n },\n async disposeSession(sessionId) {\n const inFlight = createInFlight.get(sessionId);\n createInFlight.delete(sessionId);\n let runtime = runtimesBySessionId.get(sessionId);\n if (!runtime && inFlight) {\n runtime = await inFlight.promise.catch(() => undefined);\n }\n runtimesBySessionId.delete(sessionId);\n idleTtlMsBySessionId.delete(sessionId);\n if (!runtime) {\n forgetSessionKeysForSessionId(sessionId);\n return;\n }\n forgetSessionKeysForSessionId(sessionId);\n await runtime.dispose();\n },\n async disposeAll() {\n clearIdleSweepTimer();\n const inFlightRuntimes = Array.from(createInFlight.values());\n createInFlight.clear();\n const runtimes = Array.from(runtimesBySessionId.values());\n runtimesBySessionId.clear();\n sessionIdBySessionKey.clear();\n idleTtlMsBySessionId.clear();\n const lateRuntimes = await Promise.all(\n inFlightRuntimes.map(async ({ promise }) => await promise.catch(() => undefined)),\n );\n const allRuntimes = new Set<SessionMcpRuntime>(runtimes);\n for (const runtime of lateRuntimes) {\n if (runtime) {\n allRuntimes.add(runtime);\n }\n }\n await Promise.allSettled(Array.from(allRuntimes, (runtime) => runtime.dispose()));\n },\n sweepIdleRuntimes,\n listSessionIds() {\n return Array.from(runtimesBySessionId.keys());\n },\n };\n}\n\nexport function getSessionMcpRuntimeManager(): SessionMcpRuntimeManager {\n return resolveGlobalSingleton(SESSION_MCP_RUNTIME_MANAGER_KEY, createSessionMcpRuntimeManager);\n}\n\nexport async function getOrCreateSessionMcpRuntime(params: {\n sessionId: string;\n sessionKey?: string;\n workspaceDir: string;\n cfg?: Config;\n}): Promise<SessionMcpRuntime> {\n return await getSessionMcpRuntimeManager().getOrCreate(params);\n}\n\nexport async function disposeSessionMcpRuntime(sessionId: string): Promise<void> {\n await getSessionMcpRuntimeManager().disposeSession(sessionId);\n}\n\nexport async function retireSessionMcpRuntime(params: {\n sessionId?: string | null;\n reason: string;\n onError?: (error: unknown, sessionId: string, reason: string) => void;\n}): Promise<boolean> {\n const sessionId = normalizeOptionalString(params.sessionId);\n if (!sessionId) {\n return false;\n }\n try {\n await disposeSessionMcpRuntime(sessionId);\n return true;\n } catch (error) {\n params.onError?.(error, sessionId, params.reason);\n return false;\n }\n}\n\nexport async function retireSessionMcpRuntimeForSessionKey(params: {\n sessionKey?: string | null;\n reason: string;\n onError?: (error: unknown, sessionId: string, reason: string) => void;\n}): Promise<boolean> {\n const sessionKey = normalizeOptionalString(params.sessionKey);\n if (!sessionKey) {\n return false;\n }\n const sessionId = getSessionMcpRuntimeManager().resolveSessionId(sessionKey);\n return await retireSessionMcpRuntime({\n sessionId,\n reason: params.reason,\n onError: params.onError,\n });\n}\n\nexport async function disposeAllSessionMcpRuntimes(): Promise<void> {\n await getSessionMcpRuntimeManager().disposeAll();\n}\n\nexport const __testing = {\n createSessionMcpRuntimeManager,\n async resetSessionMcpRuntimeManager() {\n await disposeAllSessionMcpRuntimes();\n },\n getCachedSessionIds() {\n return getSessionMcpRuntimeManager().listSessionIds();\n },\n resolveSessionMcpRuntimeIdleTtlMs,\n};\n"],"mappings":"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"}
@@ -10,6 +10,8 @@ export type McpServerCatalog = {
10
10
  serverName: string;
11
11
  launchSummary: string;
12
12
  toolCount: number;
13
+ resourceCount: number;
14
+ promptCount: number;
13
15
  };
14
16
  export type McpCatalogTool = {
15
17
  serverName: string;
@@ -20,11 +22,28 @@ export type McpCatalogTool = {
20
22
  inputSchema: JsonSchemaType;
21
23
  fallbackDescription: string;
22
24
  };
25
+ export type McpCatalogResource = {
26
+ serverName: string;
27
+ uri: string;
28
+ name: string;
29
+ title?: string;
30
+ description?: string;
31
+ mimeType?: string;
32
+ };
33
+ export type McpCatalogPrompt = {
34
+ serverName: string;
35
+ name: string;
36
+ title?: string;
37
+ description?: string;
38
+ argumentCount: number;
39
+ };
23
40
  export type McpToolCatalog = {
24
41
  version: number;
25
42
  generatedAt: number;
26
43
  servers: Record<string, McpServerCatalog>;
27
44
  tools: McpCatalogTool[];
45
+ resources: McpCatalogResource[];
46
+ prompts: McpCatalogPrompt[];
28
47
  };
29
48
  export type SessionMcpRuntime = {
30
49
  sessionId: string;
@@ -3,6 +3,11 @@ import { readAgentMessageContent } from "../memory/agent-message-access.js";
3
3
  import { complete } from "@earendil-works/pi-ai";
4
4
  //#region src/agent/service/btw-query.ts
5
5
  init_providers();
6
+ function textFromCompleteContent(content) {
7
+ if (typeof content === "string") return content;
8
+ if (!Array.isArray(content)) return "";
9
+ return content.filter((c) => c?.type === "text" && typeof c.text === "string").map((c) => c.text || "").join("");
10
+ }
6
11
  function formatMessagesForBtw(messages) {
7
12
  return messages.map((m) => {
8
13
  const role = m.role;
@@ -63,12 +68,15 @@ async function runBtwQuery(opts) {
63
68
  const controller = new AbortController();
64
69
  const timeoutId = setTimeout(() => controller.abort(), 9e4);
65
70
  try {
66
- const out = await complete(model, { messages: [userMessage] }, {
71
+ const text = textFromCompleteContent((await complete(model, { messages: [userMessage] }, {
67
72
  maxTokens: 2048,
68
73
  temperature: .4,
69
74
  signal: controller.signal
70
- });
71
- return { text: (Array.isArray(out.content) ? out.content.filter((c) => c.type === "text" && typeof c.text === "string").map((c) => c.text || "").join("") : "").trim() };
75
+ })).content).trim();
76
+ return text ? { text } : {
77
+ text: "",
78
+ error: "No text returned from model."
79
+ };
72
80
  } catch (err) {
73
81
  const em = err instanceof Error ? err.message : String(err);
74
82
  opts.log.warn({
@@ -1 +1 @@
1
- {"version":3,"file":"btw-query.js","names":[],"sources":["../../../../src/agent/service/btw-query.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport { complete, type UserMessage } from '@earendil-works/pi-ai';\n\nimport { readAgentMessageContent } from '../memory/agent-message-access.js';\nimport type { SessionStore } from '../../session/index.js';\nimport { resolveModel } from '../../providers/index.js';\n\ntype BtwLog = { warn: (obj: Record<string, unknown>, msg: string) => void };\n\nfunction formatMessagesForBtw(messages: AgentMessage[]): string {\n return messages\n .map((m) => {\n const role = m.role;\n let body = '';\n const raw = readAgentMessageContent(m);\n if (typeof raw === 'string') {\n body = raw;\n } else if (Array.isArray(raw)) {\n body = raw\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text')\n .map((c) => c.text || '')\n .join('\\n');\n }\n const line = `[${role}]: ${body}`;\n return line.length > 4000 ? `${line.slice(0, 4000)}…` : line;\n })\n .join('\\n\\n');\n}\n\n/** One-shot LLM answer for /btw: transcript as background only; does not persist. */\nexport async function runBtwQuery(opts: {\n sessionKey: string;\n question: string;\n sessionStore: SessionStore;\n modelForSession: string;\n log: BtwLog;\n}): Promise<{ text: string; error?: string }> {\n const q = opts.question.trim();\n if (!q) {\n return { text: '', error: 'Empty question.' };\n }\n const messages = await opts.sessionStore.load(opts.sessionKey);\n const modelRef = opts.modelForSession;\n let model;\n try {\n model = resolveModel(modelRef);\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n opts.log.warn({ err, modelRef, errorMessage: em }, 'btwQuery: model resolve failed');\n return { text: '', error: `Could not resolve model: ${modelRef}` };\n }\n\n const background = formatMessagesForBtw(messages.slice(-40));\n const systemBlock = [\n 'You are answering an ephemeral /btw side question about the current conversation.',\n 'Use the conversation only as background context.',\n 'Answer only the side question. Do not continue or complete any unfinished task from the conversation.',\n 'Do not use tools, shell, or file writes unless the question explicitly requires a tiny code snippet.',\n 'If the question can be answered briefly, answer briefly.',\n ].join('\\n');\n\n const userPrompt = [\n systemBlock,\n '',\n '---',\n 'Conversation background (read-only):',\n background || '(empty)',\n '',\n '---',\n 'Side question:',\n q,\n ].join('\\n');\n\n const userMessage: UserMessage = { role: 'user', content: userPrompt, timestamp: Date.now() };\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 90_000);\n try {\n const out = await complete(model, { messages: [userMessage] }, {\n maxTokens: 2048,\n temperature: 0.4,\n signal: controller.signal as AbortSignal,\n });\n const text = Array.isArray(out.content)\n ? out.content\n .filter(\n (c): c is { type: 'text'; text: string } =>\n c.type === 'text' && typeof (c as { text?: unknown }).text === 'string',\n )\n .map((c) => c.text || '')\n .join('')\n : '';\n return { text: text.trim() };\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n opts.log.warn({ err, sessionKey: opts.sessionKey, errorMessage: em }, 'btwQuery failed');\n return { text: '', error: em };\n } finally {\n clearTimeout(timeoutId);\n }\n}\n"],"mappings":";;;;gBAKwD;AAIxD,SAAS,qBAAqB,UAAkC;AAC9D,QAAO,SACJ,KAAK,MAAM;EACV,MAAM,OAAO,EAAE;EACf,IAAI,OAAO;EACX,MAAM,MAAM,wBAAwB,EAAE;AACtC,MAAI,OAAO,QAAQ,SACjB,QAAO;WACE,MAAM,QAAQ,IAAI,CAC3B,QAAO,IACJ,QAAQ,MAA2C,EAAE,SAAS,OAAO,CACrE,KAAK,MAAM,EAAE,QAAQ,GAAG,CACxB,KAAK,KAAK;EAEf,MAAM,OAAO,IAAI,KAAK,KAAK;AAC3B,SAAO,KAAK,SAAS,MAAO,GAAG,KAAK,MAAM,GAAG,IAAK,CAAC,KAAK;GACxD,CACD,KAAK,OAAO;;;AAIjB,eAAsB,YAAY,MAMY;CAC5C,MAAM,IAAI,KAAK,SAAS,MAAM;AAC9B,KAAI,CAAC,EACH,QAAO;EAAE,MAAM;EAAI,OAAO;EAAmB;CAE/C,MAAM,WAAW,MAAM,KAAK,aAAa,KAAK,KAAK,WAAW;CAC9D,MAAM,WAAW,KAAK;CACtB,IAAI;AACJ,KAAI;AACF,UAAQ,aAAa,SAAS;UACvB,KAAK;EACZ,MAAM,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC3D,OAAK,IAAI,KAAK;GAAE;GAAK;GAAU,cAAc;GAAI,EAAE,iCAAiC;AACpF,SAAO;GAAE,MAAM;GAAI,OAAO,4BAA4B;GAAY;;CAGpE,MAAM,aAAa,qBAAqB,SAAS,MAAM,IAAI,CAAC;CAqB5D,MAAM,cAA2B;EAAE,MAAM;EAAQ,SAZ9B;GARC;IAClB;IACA;IACA;IACA;IACA;IACD,CAAC,KAAK,KAGM;GACX;GACA;GACA;GACA,cAAc;GACd;GACA;GACA;GACA;GACD,CAAC,KAAK,KAE6D;EAAE,WAAW,KAAK,KAAK;EAAE;CAC7F,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,YAAY,iBAAiB,WAAW,OAAO,EAAE,IAAO;AAC9D,KAAI;EACF,MAAM,MAAM,MAAM,SAAS,OAAO,EAAE,UAAU,CAAC,YAAY,EAAE,EAAE;GAC7D,WAAW;GACX,aAAa;GACb,QAAQ,WAAW;GACpB,CAAC;AAUF,SAAO,EAAE,OATI,MAAM,QAAQ,IAAI,QAAQ,GACnC,IAAI,QACD,QACE,MACC,EAAE,SAAS,UAAU,OAAQ,EAAyB,SAAS,SAClE,CACA,KAAK,MAAM,EAAE,QAAQ,GAAG,CACxB,KAAK,GAAG,GACX,IACgB,MAAM,EAAE;UACrB,KAAK;EACZ,MAAM,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC3D,OAAK,IAAI,KAAK;GAAE;GAAK,YAAY,KAAK;GAAY,cAAc;GAAI,EAAE,kBAAkB;AACxF,SAAO;GAAE,MAAM;GAAI,OAAO;GAAI;WACtB;AACR,eAAa,UAAU"}
1
+ {"version":3,"file":"btw-query.js","names":[],"sources":["../../../../src/agent/service/btw-query.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport { complete, type UserMessage } from '@earendil-works/pi-ai';\n\nimport { readAgentMessageContent } from '../memory/agent-message-access.js';\nimport type { SessionStore } from '../../session/index.js';\nimport { resolveModel } from '../../providers/index.js';\n\ntype BtwLog = { warn: (obj: Record<string, unknown>, msg: string) => void };\n\nfunction textFromCompleteContent(content: unknown): string {\n if (typeof content === 'string') {\n return content;\n }\n if (!Array.isArray(content)) {\n return '';\n }\n return content\n .filter(\n (c): c is { type: 'text'; text: string } =>\n c?.type === 'text' && typeof (c as { text?: unknown }).text === 'string',\n )\n .map((c) => c.text || '')\n .join('');\n}\n\nfunction formatMessagesForBtw(messages: AgentMessage[]): string {\n return messages\n .map((m) => {\n const role = m.role;\n let body = '';\n const raw = readAgentMessageContent(m);\n if (typeof raw === 'string') {\n body = raw;\n } else if (Array.isArray(raw)) {\n body = raw\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text')\n .map((c) => c.text || '')\n .join('\\n');\n }\n const line = `[${role}]: ${body}`;\n return line.length > 4000 ? `${line.slice(0, 4000)}…` : line;\n })\n .join('\\n\\n');\n}\n\n/** One-shot LLM answer for /btw: transcript as background only; does not persist. */\nexport async function runBtwQuery(opts: {\n sessionKey: string;\n question: string;\n sessionStore: SessionStore;\n modelForSession: string;\n log: BtwLog;\n}): Promise<{ text: string; error?: string }> {\n const q = opts.question.trim();\n if (!q) {\n return { text: '', error: 'Empty question.' };\n }\n const messages = await opts.sessionStore.load(opts.sessionKey);\n const modelRef = opts.modelForSession;\n let model;\n try {\n model = resolveModel(modelRef);\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n opts.log.warn({ err, modelRef, errorMessage: em }, 'btwQuery: model resolve failed');\n return { text: '', error: `Could not resolve model: ${modelRef}` };\n }\n\n const background = formatMessagesForBtw(messages.slice(-40));\n const systemBlock = [\n 'You are answering an ephemeral /btw side question about the current conversation.',\n 'Use the conversation only as background context.',\n 'Answer only the side question. Do not continue or complete any unfinished task from the conversation.',\n 'Do not use tools, shell, or file writes unless the question explicitly requires a tiny code snippet.',\n 'If the question can be answered briefly, answer briefly.',\n ].join('\\n');\n\n const userPrompt = [\n systemBlock,\n '',\n '---',\n 'Conversation background (read-only):',\n background || '(empty)',\n '',\n '---',\n 'Side question:',\n q,\n ].join('\\n');\n\n const userMessage: UserMessage = { role: 'user', content: userPrompt, timestamp: Date.now() };\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 90_000);\n try {\n const out = await complete(model, { messages: [userMessage] }, {\n maxTokens: 2048,\n temperature: 0.4,\n signal: controller.signal as AbortSignal,\n });\n const text = textFromCompleteContent((out as { content?: unknown }).content).trim();\n return text ? { text } : { text: '', error: 'No text returned from model.' };\n } catch (err) {\n const em = err instanceof Error ? err.message : String(err);\n opts.log.warn({ err, sessionKey: opts.sessionKey, errorMessage: em }, 'btwQuery failed');\n return { text: '', error: em };\n } finally {\n clearTimeout(timeoutId);\n }\n}\n"],"mappings":";;;;gBAKwD;AAIxD,SAAS,wBAAwB,SAA0B;AACzD,KAAI,OAAO,YAAY,SACrB,QAAO;AAET,KAAI,CAAC,MAAM,QAAQ,QAAQ,CACzB,QAAO;AAET,QAAO,QACJ,QACE,MACC,GAAG,SAAS,UAAU,OAAQ,EAAyB,SAAS,SACnE,CACA,KAAK,MAAM,EAAE,QAAQ,GAAG,CACxB,KAAK,GAAG;;AAGb,SAAS,qBAAqB,UAAkC;AAC9D,QAAO,SACJ,KAAK,MAAM;EACV,MAAM,OAAO,EAAE;EACf,IAAI,OAAO;EACX,MAAM,MAAM,wBAAwB,EAAE;AACtC,MAAI,OAAO,QAAQ,SACjB,QAAO;WACE,MAAM,QAAQ,IAAI,CAC3B,QAAO,IACJ,QAAQ,MAA2C,EAAE,SAAS,OAAO,CACrE,KAAK,MAAM,EAAE,QAAQ,GAAG,CACxB,KAAK,KAAK;EAEf,MAAM,OAAO,IAAI,KAAK,KAAK;AAC3B,SAAO,KAAK,SAAS,MAAO,GAAG,KAAK,MAAM,GAAG,IAAK,CAAC,KAAK;GACxD,CACD,KAAK,OAAO;;;AAIjB,eAAsB,YAAY,MAMY;CAC5C,MAAM,IAAI,KAAK,SAAS,MAAM;AAC9B,KAAI,CAAC,EACH,QAAO;EAAE,MAAM;EAAI,OAAO;EAAmB;CAE/C,MAAM,WAAW,MAAM,KAAK,aAAa,KAAK,KAAK,WAAW;CAC9D,MAAM,WAAW,KAAK;CACtB,IAAI;AACJ,KAAI;AACF,UAAQ,aAAa,SAAS;UACvB,KAAK;EACZ,MAAM,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC3D,OAAK,IAAI,KAAK;GAAE;GAAK;GAAU,cAAc;GAAI,EAAE,iCAAiC;AACpF,SAAO;GAAE,MAAM;GAAI,OAAO,4BAA4B;GAAY;;CAGpE,MAAM,aAAa,qBAAqB,SAAS,MAAM,IAAI,CAAC;CAqB5D,MAAM,cAA2B;EAAE,MAAM;EAAQ,SAZ9B;GARC;IAClB;IACA;IACA;IACA;IACA;IACD,CAAC,KAAK,KAGM;GACX;GACA;GACA;GACA,cAAc;GACd;GACA;GACA;GACA;GACD,CAAC,KAAK,KAE6D;EAAE,WAAW,KAAK,KAAK;EAAE;CAC7F,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,YAAY,iBAAiB,WAAW,OAAO,EAAE,IAAO;AAC9D,KAAI;EAMF,MAAM,OAAO,yBAAyB,MALpB,SAAS,OAAO,EAAE,UAAU,CAAC,YAAY,EAAE,EAAE;GAC7D,WAAW;GACX,aAAa;GACb,QAAQ,WAAW;GACpB,CAAC,EACkE,QAAQ,CAAC,MAAM;AACnF,SAAO,OAAO,EAAE,MAAM,GAAG;GAAE,MAAM;GAAI,OAAO;GAAgC;UACrE,KAAK;EACZ,MAAM,KAAK,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAC3D,OAAK,IAAI,KAAK;GAAE;GAAK,YAAY,KAAK;GAAY,cAAc;GAAI,EAAE,kBAAkB;AACxF,SAAO;GAAE,MAAM;GAAI,OAAO;GAAI;WACtB;AACR,eAAa,UAAU"}
@@ -1,7 +1,7 @@
1
- import { init_session_key, parseSessionKey } from "../../routing/session-key.js";
1
+ import { getSessionMetadata } from "../../storage/sqlite/session-repository.js";
2
+ import "../../storage/sqlite/index.js";
2
3
  import { INTERNAL_OUTBOUND_DROP_CHANNEL } from "../../channels/internal-outbound.js";
3
4
  //#region src/agent/service/parse-outbound-session-key.ts
4
- init_session_key();
5
5
  /**
6
6
  * Map a session key to outbound channel routing (heartbeat/cron/virtual keys included).
7
7
  */
@@ -21,18 +21,23 @@ function parseOutboundSessionKey(sessionKey, config) {
21
21
  chatId: parts.slice(1).join(":") || "heartbeat"
22
22
  };
23
23
  }
24
- const parsed = parseSessionKey(sessionKey);
25
- if (parsed) return {
26
- channel: parsed.source,
27
- chatId: parsed.peerId
24
+ const metadata = getSessionMetadata(sessionKey);
25
+ const routing = metadata?.routing;
26
+ if (routing?.source && routing.peerId) return {
27
+ channel: routing.source,
28
+ chatId: routing.peerId
29
+ };
30
+ if (metadata?.sourceChannel && metadata.sourceChatId) return {
31
+ channel: metadata.sourceChannel,
32
+ chatId: metadata.sourceChatId
28
33
  };
29
34
  if (first === "cron") return {
30
35
  channel: INTERNAL_OUTBOUND_DROP_CHANNEL,
31
36
  chatId: parts.slice(1).join(":") || "cron"
32
37
  };
33
38
  return {
34
- channel: first,
35
- chatId: parts.slice(1).join(":") || "direct"
39
+ channel: "cli",
40
+ chatId: "main"
36
41
  };
37
42
  }
38
43
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"parse-outbound-session-key.js","names":["parseRoutingSessionKey"],"sources":["../../../../src/agent/service/parse-outbound-session-key.ts"],"sourcesContent":["import type { Config } from '../../config/schema.js';\nimport { INTERNAL_OUTBOUND_DROP_CHANNEL } from '../../channels/internal-outbound.js';\nimport { parseSessionKey as parseRoutingSessionKey } from '../../routing/session-key.js';\n\n/**\n * Map a session key to outbound channel routing (heartbeat/cron/virtual keys included).\n */\nexport function parseOutboundSessionKey(\n sessionKey: string,\n config: Config | undefined,\n): { channel: string; chatId: string } {\n const parts = sessionKey.split(':').filter(Boolean);\n const first = parts[0] || 'cli';\n\n if (first === 'heartbeat') {\n const hb = config?.gateway?.heartbeat;\n const target = hb?.target?.trim();\n const targetChatId = hb?.targetChatId?.trim();\n if (target && targetChatId) {\n return { channel: target, chatId: targetChatId };\n }\n return { channel: INTERNAL_OUTBOUND_DROP_CHANNEL, chatId: parts.slice(1).join(':') || 'heartbeat' };\n }\n\n const parsed = parseRoutingSessionKey(sessionKey);\n if (parsed) {\n return { channel: parsed.source, chatId: parsed.peerId };\n }\n\n if (first === 'cron') {\n return { channel: INTERNAL_OUTBOUND_DROP_CHANNEL, chatId: parts.slice(1).join(':') || 'cron' };\n }\n\n return {\n channel: first,\n chatId: parts.slice(1).join(':') || 'direct',\n };\n}\n"],"mappings":";;;kBAEyF;;;;AAKzF,SAAgB,wBACd,YACA,QACqC;CACrC,MAAM,QAAQ,WAAW,MAAM,IAAI,CAAC,OAAO,QAAQ;CACnD,MAAM,QAAQ,MAAM,MAAM;AAE1B,KAAI,UAAU,aAAa;EACzB,MAAM,KAAK,QAAQ,SAAS;EAC5B,MAAM,SAAS,IAAI,QAAQ,MAAM;EACjC,MAAM,eAAe,IAAI,cAAc,MAAM;AAC7C,MAAI,UAAU,aACZ,QAAO;GAAE,SAAS;GAAQ,QAAQ;GAAc;AAElD,SAAO;GAAE,SAAS;GAAgC,QAAQ,MAAM,MAAM,EAAE,CAAC,KAAK,IAAI,IAAI;GAAa;;CAGrG,MAAM,SAASA,gBAAuB,WAAW;AACjD,KAAI,OACF,QAAO;EAAE,SAAS,OAAO;EAAQ,QAAQ,OAAO;EAAQ;AAG1D,KAAI,UAAU,OACZ,QAAO;EAAE,SAAS;EAAgC,QAAQ,MAAM,MAAM,EAAE,CAAC,KAAK,IAAI,IAAI;EAAQ;AAGhG,QAAO;EACL,SAAS;EACT,QAAQ,MAAM,MAAM,EAAE,CAAC,KAAK,IAAI,IAAI;EACrC"}
1
+ {"version":3,"file":"parse-outbound-session-key.js","names":[],"sources":["../../../../src/agent/service/parse-outbound-session-key.ts"],"sourcesContent":["import type { Config } from '../../config/schema.js';\nimport { INTERNAL_OUTBOUND_DROP_CHANNEL } from '../../channels/internal-outbound.js';\nimport { getSessionMetadata } from '../../storage/sqlite/index.js';\n\n/**\n * Map a session key to outbound channel routing (heartbeat/cron/virtual keys included).\n */\nexport function parseOutboundSessionKey(\n sessionKey: string,\n config: Config | undefined,\n): { channel: string; chatId: string } {\n const parts = sessionKey.split(':').filter(Boolean);\n const first = parts[0] || 'cli';\n\n if (first === 'heartbeat') {\n const hb = config?.gateway?.heartbeat;\n const target = hb?.target?.trim();\n const targetChatId = hb?.targetChatId?.trim();\n if (target && targetChatId) {\n return { channel: target, chatId: targetChatId };\n }\n return { channel: INTERNAL_OUTBOUND_DROP_CHANNEL, chatId: parts.slice(1).join(':') || 'heartbeat' };\n }\n\n const metadata = getSessionMetadata(sessionKey);\n const routing = metadata?.routing;\n if (routing?.source && routing.peerId) {\n return { channel: routing.source, chatId: routing.peerId };\n }\n if (metadata?.sourceChannel && metadata.sourceChatId) {\n return { channel: metadata.sourceChannel, chatId: metadata.sourceChatId };\n }\n\n if (first === 'cron') {\n return { channel: INTERNAL_OUTBOUND_DROP_CHANNEL, chatId: parts.slice(1).join(':') || 'cron' };\n }\n\n return { channel: 'cli', chatId: 'main' };\n}\n"],"mappings":";;;;;;;AAOA,SAAgB,wBACd,YACA,QACqC;CACrC,MAAM,QAAQ,WAAW,MAAM,IAAI,CAAC,OAAO,QAAQ;CACnD,MAAM,QAAQ,MAAM,MAAM;AAE1B,KAAI,UAAU,aAAa;EACzB,MAAM,KAAK,QAAQ,SAAS;EAC5B,MAAM,SAAS,IAAI,QAAQ,MAAM;EACjC,MAAM,eAAe,IAAI,cAAc,MAAM;AAC7C,MAAI,UAAU,aACZ,QAAO;GAAE,SAAS;GAAQ,QAAQ;GAAc;AAElD,SAAO;GAAE,SAAS;GAAgC,QAAQ,MAAM,MAAM,EAAE,CAAC,KAAK,IAAI,IAAI;GAAa;;CAGrG,MAAM,WAAW,mBAAmB,WAAW;CAC/C,MAAM,UAAU,UAAU;AAC1B,KAAI,SAAS,UAAU,QAAQ,OAC7B,QAAO;EAAE,SAAS,QAAQ;EAAQ,QAAQ,QAAQ;EAAQ;AAE5D,KAAI,UAAU,iBAAiB,SAAS,aACtC,QAAO;EAAE,SAAS,SAAS;EAAe,QAAQ,SAAS;EAAc;AAG3E,KAAI,UAAU,OACZ,QAAO;EAAE,SAAS;EAAgC,QAAQ,MAAM,MAAM,EAAE,CAAC,KAAK,IAAI,IAAI;EAAQ;AAGhG,QAAO;EAAE,SAAS;EAAO,QAAQ;EAAQ"}
@@ -8,10 +8,10 @@ import type { InboundAttachmentInput, MediaRef } from '../../channels/attachment
8
8
  export type RunProcessDirectDeps = {
9
9
  log: ProcessDirectStreamLog;
10
10
  config: Config;
11
- parseSessionKey: (sessionKey: string) => {
11
+ resolveSessionEndpoint: (sessionKey: string) => Promise<{
12
12
  channel: string;
13
13
  chatId: string;
14
- };
14
+ }>;
15
15
  initSessionContext: (sessionKey: string, channel: string, chatId: string) => void;
16
16
  hydrateSessionWorkspaceFromStore: (sessionKey: string) => Promise<void>;
17
17
  hydrateSessionModelFromStore: (sessionKey: string) => Promise<void>;
@@ -5,7 +5,7 @@ import { buildDirectUserMessageContent } from "./build-direct-message-content.js
5
5
  import { hydratePerTurnState, runDirectAgentTurn, tryRunSlashCommand } from "./direct-turn-helpers.js";
6
6
  //#region src/agent/service/process-direct-one-shot.ts
7
7
  async function runProcessDirect(deps, input) {
8
- const { channel, chatId } = deps.parseSessionKey(input.sessionKey);
8
+ const { channel, chatId } = await deps.resolveSessionEndpoint(input.sessionKey);
9
9
  deps.initSessionContext(input.sessionKey, channel, chatId);
10
10
  try {
11
11
  let turnBody = input.content;
@@ -1 +1 @@
1
- {"version":3,"file":"process-direct-one-shot.js","names":[],"sources":["../../../../src/agent/service/process-direct-one-shot.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { CommandHandler } from '../messaging/command-handler.js';\nimport type { AgentInstanceGateway } from '../agent-instance-gateway.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { SessionStore } from '../../session/index.js';\nimport type { Config } from '../../config/schema.js';\nimport { initSessionTurn } from '../../session/index.js';\nimport {\n buildDirectUserMessageContent,\n} from './build-direct-message-content.js';\nimport type { ProcessDirectStreamLog } from './process-direct-streaming.js';\nimport type {\n InboundAttachmentInput,\n MediaRef,\n} from '../../channels/attachments/inbound-persist.js';\nimport {\n hydratePerTurnState,\n runDirectAgentTurn,\n tryRunSlashCommand,\n} from './direct-turn-helpers.js';\nimport {\n clearPendingTranscriptUserMessage,\n setPendingTranscriptUserMessage,\n type TranscriptUserMessage,\n} from '../inbound/attachment-pipeline.js';\n\nexport type RunProcessDirectDeps = {\n log: ProcessDirectStreamLog;\n config: Config;\n parseSessionKey: (sessionKey: string) => { channel: string; chatId: string };\n initSessionContext: (sessionKey: string, channel: string, chatId: string) => void;\n hydrateSessionWorkspaceFromStore: (sessionKey: string) => Promise<void>;\n hydrateSessionModelFromStore: (sessionKey: string) => Promise<void>;\n agentManager: AgentInstanceGateway;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n applyResolvedThinkingLevel: (sessionKey: string, thinking?: string | null) => Promise<void>;\n prepareInboundAttachments: (\n sessionKey: string,\n attachments?: InboundAttachmentInput[],\n ) => Promise<MediaRef[] | undefined>;\n commandHandler: Pick<CommandHandler, 'executeCommandAndAggregateReply'>;\n onTurnComplete?: (sessionKey: string, lastAssistantText?: string) => void;\n endDirectRequestContext: () => void;\n resetSession: (sessionKey: string) => Promise<{ sessionId: string; previousSessionId: string } | null>;\n};\n\nexport async function runProcessDirect(\n deps: RunProcessDirectDeps,\n input: {\n content: string;\n sessionKey: string;\n attachments?: InboundAttachmentInput[];\n thinking?: string;\n },\n): Promise<string> {\n const { channel, chatId } = deps.parseSessionKey(input.sessionKey);\n deps.initSessionContext(input.sessionKey, channel, chatId);\n\n try {\n let turnBody = input.content;\n let resetTriggeredAtInit = false;\n const turn = await initSessionTurn({\n cfg: deps.config,\n sessionKey: input.sessionKey,\n body: input.content,\n resetSession: deps.resetSession,\n });\n resetTriggeredAtInit = turn.resetTriggered;\n if (turn.bareReset && turn.ackMessage) {\n return turn.ackMessage;\n }\n turnBody = turn.bodyStripped;\n\n await hydratePerTurnState(deps, input.sessionKey, input.thinking);\n const prepared = await deps.prepareInboundAttachments(input.sessionKey, input.attachments);\n\n const slash = await tryRunSlashCommand(\n deps,\n { sessionKey: input.sessionKey, channel, chatId },\n turnBody,\n { skipResetCommands: resetTriggeredAtInit },\n );\n if (slash.matched) {\n const trimmed = slash.aggregatedText.trim();\n if (trimmed) {\n await deps.sessionStore.appendTranscriptMessage(input.sessionKey, {\n role: 'assistant',\n content: [{ type: 'text', text: trimmed }],\n timestamp: Date.now(),\n } as AgentMessage);\n }\n return slash.aggregatedText ?? '';\n }\n\n const textForDirect = turnBody.trimStart().startsWith('/skill:')\n ? deps.agentManager.expandSkillUserText(turnBody)\n : turnBody;\n const userMessage = await buildDirectUserMessageContent({\n content: textForDirect,\n attachments: prepared,\n sessionKey: input.sessionKey,\n config: deps.config,\n agentManager: deps.agentManager,\n modelManager: deps.modelManager,\n });\n\n const pendingUserMessage = userMessage as TranscriptUserMessage;\n setPendingTranscriptUserMessage(input.sessionKey, pendingUserMessage);\n\n const result = await (async () => {\n try {\n return await runDirectAgentTurn(\n { ...deps, config: deps.config },\n {\n sessionKey: input.sessionKey,\n userMessage,\n },\n );\n } finally {\n clearPendingTranscriptUserMessage(input.sessionKey, pendingUserMessage);\n }\n })();\n\n if (result.lastAssistantText) {\n deps.onTurnComplete?.(input.sessionKey, result.lastAssistantText);\n }\n\n return result.lastAssistantText ?? '';\n } finally {\n deps.endDirectRequestContext();\n }\n}\n"],"mappings":";;;;;;AAgDA,eAAsB,iBACpB,MACA,OAMiB;CACjB,MAAM,EAAE,SAAS,WAAW,KAAK,gBAAgB,MAAM,WAAW;AAClE,MAAK,mBAAmB,MAAM,YAAY,SAAS,OAAO;AAE1D,KAAI;EACF,IAAI,WAAW,MAAM;EACrB,IAAI,uBAAuB;EAC3B,MAAM,OAAO,MAAM,gBAAgB;GACjC,KAAK,KAAK;GACV,YAAY,MAAM;GAClB,MAAM,MAAM;GACZ,cAAc,KAAK;GACpB,CAAC;AACF,yBAAuB,KAAK;AAC5B,MAAI,KAAK,aAAa,KAAK,WACzB,QAAO,KAAK;AAEd,aAAW,KAAK;AAEhB,QAAM,oBAAoB,MAAM,MAAM,YAAY,MAAM,SAAS;EACjE,MAAM,WAAW,MAAM,KAAK,0BAA0B,MAAM,YAAY,MAAM,YAAY;EAE1F,MAAM,QAAQ,MAAM,mBAClB,MACA;GAAE,YAAY,MAAM;GAAY;GAAS;GAAQ,EACjD,UACA,EAAE,mBAAmB,sBAAsB,CAC5C;AACD,MAAI,MAAM,SAAS;GACjB,MAAM,UAAU,MAAM,eAAe,MAAM;AAC3C,OAAI,QACF,OAAM,KAAK,aAAa,wBAAwB,MAAM,YAAY;IAChE,MAAM;IACN,SAAS,CAAC;KAAE,MAAM;KAAQ,MAAM;KAAS,CAAC;IAC1C,WAAW,KAAK,KAAK;IACtB,CAAiB;AAEpB,UAAO,MAAM,kBAAkB;;EAMjC,MAAM,cAAc,MAAM,8BAA8B;GACtD,SAJoB,SAAS,WAAW,CAAC,WAAW,UAAU,GAC5D,KAAK,aAAa,oBAAoB,SAAS,GAC/C;GAGF,aAAa;GACb,YAAY,MAAM;GAClB,QAAQ,KAAK;GACb,cAAc,KAAK;GACnB,cAAc,KAAK;GACpB,CAAC;EAEF,MAAM,qBAAqB;AAC3B,kCAAgC,MAAM,YAAY,mBAAmB;EAErE,MAAM,SAAS,OAAO,YAAY;AAChC,OAAI;AACF,WAAO,MAAM,mBACX;KAAE,GAAG;KAAM,QAAQ,KAAK;KAAQ,EAChC;KACE,YAAY,MAAM;KAClB;KACD,CACF;aACO;AACR,sCAAkC,MAAM,YAAY,mBAAmB;;MAEvE;AAEJ,MAAI,OAAO,kBACT,MAAK,iBAAiB,MAAM,YAAY,OAAO,kBAAkB;AAGnE,SAAO,OAAO,qBAAqB;WAC3B;AACR,OAAK,yBAAyB"}
1
+ {"version":3,"file":"process-direct-one-shot.js","names":[],"sources":["../../../../src/agent/service/process-direct-one-shot.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { CommandHandler } from '../messaging/command-handler.js';\nimport type { AgentInstanceGateway } from '../agent-instance-gateway.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { SessionStore } from '../../session/index.js';\nimport type { Config } from '../../config/schema.js';\nimport { initSessionTurn } from '../../session/index.js';\nimport {\n buildDirectUserMessageContent,\n} from './build-direct-message-content.js';\nimport type { ProcessDirectStreamLog } from './process-direct-streaming.js';\nimport type {\n InboundAttachmentInput,\n MediaRef,\n} from '../../channels/attachments/inbound-persist.js';\nimport {\n hydratePerTurnState,\n runDirectAgentTurn,\n tryRunSlashCommand,\n} from './direct-turn-helpers.js';\nimport {\n clearPendingTranscriptUserMessage,\n setPendingTranscriptUserMessage,\n type TranscriptUserMessage,\n} from '../inbound/attachment-pipeline.js';\n\nexport type RunProcessDirectDeps = {\n log: ProcessDirectStreamLog;\n config: Config;\n resolveSessionEndpoint: (sessionKey: string) => Promise<{ channel: string; chatId: string }>;\n initSessionContext: (sessionKey: string, channel: string, chatId: string) => void;\n hydrateSessionWorkspaceFromStore: (sessionKey: string) => Promise<void>;\n hydrateSessionModelFromStore: (sessionKey: string) => Promise<void>;\n agentManager: AgentInstanceGateway;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n applyResolvedThinkingLevel: (sessionKey: string, thinking?: string | null) => Promise<void>;\n prepareInboundAttachments: (\n sessionKey: string,\n attachments?: InboundAttachmentInput[],\n ) => Promise<MediaRef[] | undefined>;\n commandHandler: Pick<CommandHandler, 'executeCommandAndAggregateReply'>;\n onTurnComplete?: (sessionKey: string, lastAssistantText?: string) => void;\n endDirectRequestContext: () => void;\n resetSession: (sessionKey: string) => Promise<{ sessionId: string; previousSessionId: string } | null>;\n};\n\nexport async function runProcessDirect(\n deps: RunProcessDirectDeps,\n input: {\n content: string;\n sessionKey: string;\n attachments?: InboundAttachmentInput[];\n thinking?: string;\n },\n): Promise<string> {\n const { channel, chatId } = await deps.resolveSessionEndpoint(input.sessionKey);\n deps.initSessionContext(input.sessionKey, channel, chatId);\n\n try {\n let turnBody = input.content;\n let resetTriggeredAtInit = false;\n const turn = await initSessionTurn({\n cfg: deps.config,\n sessionKey: input.sessionKey,\n body: input.content,\n resetSession: deps.resetSession,\n });\n resetTriggeredAtInit = turn.resetTriggered;\n if (turn.bareReset && turn.ackMessage) {\n return turn.ackMessage;\n }\n turnBody = turn.bodyStripped;\n\n await hydratePerTurnState(deps, input.sessionKey, input.thinking);\n const prepared = await deps.prepareInboundAttachments(input.sessionKey, input.attachments);\n\n const slash = await tryRunSlashCommand(\n deps,\n { sessionKey: input.sessionKey, channel, chatId },\n turnBody,\n { skipResetCommands: resetTriggeredAtInit },\n );\n if (slash.matched) {\n const trimmed = slash.aggregatedText.trim();\n if (trimmed) {\n await deps.sessionStore.appendTranscriptMessage(input.sessionKey, {\n role: 'assistant',\n content: [{ type: 'text', text: trimmed }],\n timestamp: Date.now(),\n } as AgentMessage);\n }\n return slash.aggregatedText ?? '';\n }\n\n const textForDirect = turnBody.trimStart().startsWith('/skill:')\n ? deps.agentManager.expandSkillUserText(turnBody)\n : turnBody;\n const userMessage = await buildDirectUserMessageContent({\n content: textForDirect,\n attachments: prepared,\n sessionKey: input.sessionKey,\n config: deps.config,\n agentManager: deps.agentManager,\n modelManager: deps.modelManager,\n });\n\n const pendingUserMessage = userMessage as TranscriptUserMessage;\n setPendingTranscriptUserMessage(input.sessionKey, pendingUserMessage);\n\n const result = await (async () => {\n try {\n return await runDirectAgentTurn(\n { ...deps, config: deps.config },\n {\n sessionKey: input.sessionKey,\n userMessage,\n },\n );\n } finally {\n clearPendingTranscriptUserMessage(input.sessionKey, pendingUserMessage);\n }\n })();\n\n if (result.lastAssistantText) {\n deps.onTurnComplete?.(input.sessionKey, result.lastAssistantText);\n }\n\n return result.lastAssistantText ?? '';\n } finally {\n deps.endDirectRequestContext();\n }\n}\n"],"mappings":";;;;;;AAgDA,eAAsB,iBACpB,MACA,OAMiB;CACjB,MAAM,EAAE,SAAS,WAAW,MAAM,KAAK,uBAAuB,MAAM,WAAW;AAC/E,MAAK,mBAAmB,MAAM,YAAY,SAAS,OAAO;AAE1D,KAAI;EACF,IAAI,WAAW,MAAM;EACrB,IAAI,uBAAuB;EAC3B,MAAM,OAAO,MAAM,gBAAgB;GACjC,KAAK,KAAK;GACV,YAAY,MAAM;GAClB,MAAM,MAAM;GACZ,cAAc,KAAK;GACpB,CAAC;AACF,yBAAuB,KAAK;AAC5B,MAAI,KAAK,aAAa,KAAK,WACzB,QAAO,KAAK;AAEd,aAAW,KAAK;AAEhB,QAAM,oBAAoB,MAAM,MAAM,YAAY,MAAM,SAAS;EACjE,MAAM,WAAW,MAAM,KAAK,0BAA0B,MAAM,YAAY,MAAM,YAAY;EAE1F,MAAM,QAAQ,MAAM,mBAClB,MACA;GAAE,YAAY,MAAM;GAAY;GAAS;GAAQ,EACjD,UACA,EAAE,mBAAmB,sBAAsB,CAC5C;AACD,MAAI,MAAM,SAAS;GACjB,MAAM,UAAU,MAAM,eAAe,MAAM;AAC3C,OAAI,QACF,OAAM,KAAK,aAAa,wBAAwB,MAAM,YAAY;IAChE,MAAM;IACN,SAAS,CAAC;KAAE,MAAM;KAAQ,MAAM;KAAS,CAAC;IAC1C,WAAW,KAAK,KAAK;IACtB,CAAiB;AAEpB,UAAO,MAAM,kBAAkB;;EAMjC,MAAM,cAAc,MAAM,8BAA8B;GACtD,SAJoB,SAAS,WAAW,CAAC,WAAW,UAAU,GAC5D,KAAK,aAAa,oBAAoB,SAAS,GAC/C;GAGF,aAAa;GACb,YAAY,MAAM;GAClB,QAAQ,KAAK;GACb,cAAc,KAAK;GACnB,cAAc,KAAK;GACpB,CAAC;EAEF,MAAM,qBAAqB;AAC3B,kCAAgC,MAAM,YAAY,mBAAmB;EAErE,MAAM,SAAS,OAAO,YAAY;AAChC,OAAI;AACF,WAAO,MAAM,mBACX;KAAE,GAAG;KAAM,QAAQ,KAAK;KAAQ,EAChC;KACE,YAAY,MAAM;KAClB;KACD,CACF;aACO;AACR,sCAAkC,MAAM,YAAY,mBAAmB;;MAEvE;AAEJ,MAAI,OAAO,kBACT,MAAK,iBAAiB,MAAM,YAAY,OAAO,kBAAkB;AAGnE,SAAO,OAAO,qBAAqB;WAC3B;AACR,OAAK,yBAAyB"}
@@ -15,10 +15,10 @@ export type ProcessDirectStreamLog = {
15
15
  };
16
16
  export interface ProcessDirectStreamingDeps {
17
17
  log: ProcessDirectStreamLog;
18
- parseSessionKey: (sessionKey: string) => {
18
+ resolveSessionEndpoint: (sessionKey: string) => Promise<{
19
19
  channel: string;
20
20
  chatId: string;
21
- };
21
+ }>;
22
22
  initDirectStreamingSession: (sessionKey: string, channel: string, chatId: string) => SessionContext;
23
23
  registerWebchatSsePublisher: (sessionKey: string, publisher: (event: {
24
24
  type: string;
@@ -42,7 +42,7 @@ function pushAssistantReceipt(queue, text, runId) {
42
42
  }
43
43
  async function* runProcessDirectStreaming(deps, input) {
44
44
  const sessionKey = input.sessionKey ?? "agent:main:main";
45
- const { channel, chatId } = deps.parseSessionKey(sessionKey);
45
+ const { channel, chatId } = await deps.resolveSessionEndpoint(sessionKey);
46
46
  const context = deps.initDirectStreamingSession(sessionKey, channel, chatId);
47
47
  const queue = new AsyncQueue();
48
48
  let reasoningLevel = "stream";
@@ -219,6 +219,15 @@ async function* runProcessDirectStreaming(deps, input) {
219
219
  content: formatStreamError(err instanceof Error ? err.message : String(err))
220
220
  });
221
221
  } finally {
222
+ if (!userAborted && channel === "webchat") try {
223
+ const ttsAudioEvent = await deps.maybeEmitWebchatTts(sessionKey, inboundVoice);
224
+ if (ttsAudioEvent) queue.push(ttsAudioEvent);
225
+ } catch (ttsErr) {
226
+ deps.log.warn({
227
+ err: ttsErr,
228
+ sessionKey
229
+ }, "Failed to emit TTS audio before stream close");
230
+ }
222
231
  queue.close();
223
232
  }
224
233
  })();
@@ -254,10 +263,6 @@ async function* runProcessDirectStreaming(deps, input) {
254
263
  sessionKey
255
264
  }, "Failed to persist webchat slash command receipt");
256
265
  }
257
- if (!userAborted) {
258
- const ttsAudioEvent = await deps.maybeEmitWebchatTts(sessionKey, inboundVoice);
259
- if (ttsAudioEvent) yield ttsAudioEvent;
260
- }
261
266
  deps.recordPersistentGoalStreamOutcome?.(sessionKey, { skipPersistentGoalPostTurn: ranSlashCommand });
262
267
  } finally {
263
268
  if (channel === "webchat") deps.unregisterWebchatSsePublisher(sessionKey);
@@ -1 +1 @@
1
- {"version":3,"file":"process-direct-streaming.js","names":[],"sources":["../../../../src/agent/service/process-direct-streaming.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { ImageContent } from '@earendil-works/pi-ai';\n\nimport type { Config } from '../../config/schema.js';\nimport type { InboundAttachmentInput, MediaRef } from '../../channels/attachments/inbound-persist.js';\nimport { readAgentMessageContent } from '../memory/agent-message-access.js';\nimport {\n isVoiceLikeAttachment,\n mergeVoiceTranscriptsIntoUserText,\n mergeSttConfigFromAppConfig,\n} from '../../channels/attachments/voice-stt-webchat.js';\nimport {\n resolveEffectiveReasoningLevel,\n initSessionTurn,\n type SessionConfigStore,\n type SessionStore,\n} from '../../session/index.js';\nimport type { SessionContext } from '../session/index.js';\nimport { applyReasoningVisibilityToSseEvent } from '../streaming/reasoning-visibility-sse.js';\nimport type { ReasoningLevel } from '../transcript/thinking-types.js';\nimport { formatAgentRunErrorForClient } from '../client-error-format.js';\nimport { abortEmbeddedRun } from '../embedded/runs.js';\nimport type { AgentInstanceGateway } from '../agent-instance-gateway.js';\nimport type { CommandHandler } from '../messaging/command-handler.js';\nimport type { ModelManager } from '../models/index.js';\nimport { injectSourceContextIntoUserMessage } from '../source-context/injector.js';\nimport { isSessionSourceBinding, type AgentSourceContextResolver } from '../source-context/types.js';\n\nimport { AsyncQueue } from './async-queue.js';\nimport {\n hydratePerTurnState,\n runDirectAgentTurn,\n tryRunSlashCommand,\n} from './direct-turn-helpers.js';\nimport {\n clearPendingTranscriptUserMessage,\n setPendingTranscriptUserMessage,\n type TranscriptUserMessage,\n} from '../inbound/attachment-pipeline.js';\n\nexport type DirectStreamInboundAttachment = InboundAttachmentInput;\n\nexport type ProcessDirectStreamLog = {\n info: (obj: Record<string, unknown>, msg: string) => void;\n warn: (obj: Record<string, unknown>, msg: string) => void;\n debug?: (obj: Record<string, unknown>, msg: string) => void;\n};\n\nexport interface ProcessDirectStreamingDeps {\n log: ProcessDirectStreamLog;\n parseSessionKey: (sessionKey: string) => { channel: string; chatId: string };\n initDirectStreamingSession: (\n sessionKey: string,\n channel: string,\n chatId: string,\n ) => SessionContext;\n registerWebchatSsePublisher: (\n sessionKey: string,\n publisher: (event: { type: string; [key: string]: unknown }) => void,\n ) => void;\n unregisterWebchatSsePublisher: (sessionKey: string) => void;\n agentManager: AgentInstanceGateway;\n hydrateSessionWorkspaceFromStore: (sessionKey: string) => Promise<void>;\n hydrateSessionModelFromStore: (sessionKey: string) => Promise<void>;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n applyResolvedThinkingLevel: (sessionKey: string, thinking?: string | null) => Promise<void>;\n getConfig: () => Config | undefined;\n sessionConfigStore: SessionConfigStore;\n commandHandler: Pick<CommandHandler, 'executeCommandAndAggregateReply'>;\n prepareInboundAttachments: (\n sessionKey: string,\n attachments?: DirectStreamInboundAttachment[],\n ) => Promise<MediaRef[] | undefined>;\n buildTranscriptUserMessage: (\n content: string,\n prepared: MediaRef[] | undefined,\n sessionKey: string,\n ) => Promise<TranscriptUserMessage>;\n recordPersistentGoalStreamOutcome?: (\n sessionKey: string,\n outcome: { skipPersistentGoalPostTurn: boolean },\n ) => void;\n onTurnComplete?: (sessionKey: string, lastAssistantText?: string) => void;\n enqueueProvisionalSessionTitle?: (sessionKey: string, userText: string) => void;\n /** Disk-only transcript sync (slash receipt already streamed as tokens). */\n reloadWebchatTranscript?: (sessionKey: string) => void;\n maybeEmitWebchatTts: (\n sessionKey: string,\n hadInboundVoice: boolean,\n ) => Promise<{ type: 'tts_audio'; uri: string; mimeType: string; name: string } | null>;\n endDirectRequestContext: () => void;\n resetSession: (sessionKey: string) => Promise<{ sessionId: string; previousSessionId: string } | null>;\n sourceContextResolver?: AgentSourceContextResolver;\n}\n\nexport interface ProcessDirectStreamingInput {\n content: string;\n sessionKey?: string;\n attachments?: DirectStreamInboundAttachment[];\n thinking?: string;\n signal?: AbortSignal;\n runId?: string;\n}\n\nexport type ProcessDirectStreamingSseEvent = { type: string; [key: string]: unknown };\n\nfunction makeAssistantReceiptMessage(text: string): AgentMessage {\n return {\n role: 'assistant',\n content: [{ type: 'text', text }],\n timestamp: Date.now(),\n } as AgentMessage;\n}\n\nfunction pushAssistantReceipt(\n queue: AsyncQueue<ProcessDirectStreamingSseEvent>,\n text: string,\n runId?: string,\n): void {\n const message = makeAssistantReceiptMessage(text);\n queue.push({ type: 'message_start', runId, message });\n queue.push({ type: 'message_update', runId, message });\n queue.push({ type: 'message_end', runId, message });\n}\n\nexport async function* runProcessDirectStreaming(\n deps: ProcessDirectStreamingDeps,\n input: ProcessDirectStreamingInput,\n): AsyncGenerator<ProcessDirectStreamingSseEvent, void, unknown> {\n const sessionKey = input.sessionKey ?? 'agent:main:main';\n const { channel, chatId } = deps.parseSessionKey(sessionKey);\n const context = deps.initDirectStreamingSession(sessionKey, channel, chatId);\n\n const queue = new AsyncQueue<ProcessDirectStreamingSseEvent>();\n let reasoningLevel: ReasoningLevel = 'stream';\n\n const pushVisible = (event: ProcessDirectStreamingSseEvent) => {\n const visible = applyReasoningVisibilityToSseEvent(event, reasoningLevel);\n if (visible !== null) {\n queue.push(visible);\n }\n };\n\n const formatStreamError = (raw: string): string => {\n let provider: string | undefined;\n let modelRef: string | undefined;\n try {\n const resolved = deps.modelManager.getResolvedModelForSession(sessionKey);\n provider = resolved.provider;\n modelRef = deps.modelManager.getModelForSession(sessionKey);\n } catch {\n /* ignore — format without provider context */\n }\n return formatAgentRunErrorForClient(raw, { provider, modelRef });\n };\n\n if (channel === 'webchat') {\n deps.registerWebchatSsePublisher(sessionKey, pushVisible);\n }\n\n const signal = input.signal;\n let userAborted = false;\n let abortHandled = false;\n let inboundVoice = false;\n let ranSlashCommand = false;\n let mergedUserText = input.content;\n let webchatSlashReceipt: string | undefined;\n let slashCommandMetadata: Record<string, unknown> | undefined;\n\n const taskPromise = (async () => {\n try {\n const cfg = deps.getConfig();\n let turnBody = input.content;\n let resetTriggeredAtInit = false;\n if (cfg) {\n const turn = await initSessionTurn({\n cfg,\n sessionKey,\n body: input.content,\n resetSession: deps.resetSession,\n });\n resetTriggeredAtInit = turn.resetTriggered;\n if (turn.bareReset && turn.ackMessage) {\n ranSlashCommand = true;\n webchatSlashReceipt = turn.ackMessage;\n pushAssistantReceipt(queue, turn.ackMessage, input.runId);\n return;\n }\n turnBody = turn.bodyStripped;\n if (turn.isNewSession) {\n deps.log.debug(\n {\n sessionKey,\n sessionId: turn.sessionId,\n previousSessionId: turn.previousSessionId,\n resetTriggered: turn.resetTriggered,\n staleRollover: turn.staleRollover,\n },\n 'Session reset boundary at direct turn start',\n );\n }\n }\n\n await hydratePerTurnState(deps, sessionKey, input.thinking);\n {\n const defReason = (deps.getConfig()?.agents?.defaults?.reasoningDefault ?? 'stream') as ReasoningLevel;\n reasoningLevel = await resolveEffectiveReasoningLevel(deps.sessionConfigStore, sessionKey, defReason);\n }\n\n const prepared = await deps.prepareInboundAttachments(sessionKey, input.attachments);\n\n const sttCfg = mergeSttConfigFromAppConfig(deps.getConfig()?.tools?.media?.audio, deps.getConfig()?.tools?.media);\n const voiceMerge = await mergeVoiceTranscriptsIntoUserText(prepared, turnBody, sttCfg);\n mergedUserText = voiceMerge.text;\n inboundVoice = voiceMerge.inboundVoice;\n\n if (inboundVoice) {\n const transcriptParts = [\n voiceMerge.voiceTranscripts.filter(Boolean).join('\\n'),\n turnBody.trim(),\n ].filter(Boolean);\n const voiceMedia = (prepared ?? []).filter(isVoiceLikeAttachment).map((att) => ({\n uri: att.uri,\n mimeType: att.mimeType,\n name: att.name,\n }));\n pushVisible({\n type: 'user_transcript',\n text: transcriptParts.join('\\n\\n'),\n media: voiceMedia,\n });\n }\n\n const armAbort = () => {\n if (abortHandled) {\n return;\n }\n abortHandled = true;\n userAborted = true;\n void abortEmbeddedRun(sessionKey);\n queue.close();\n };\n if (signal) {\n if (signal.aborted) {\n armAbort();\n return;\n }\n signal.addEventListener('abort', armAbort, { once: true });\n }\n\n const slash = await tryRunSlashCommand(\n deps,\n {\n sessionKey,\n channel,\n chatId,\n senderId: context.senderId,\n isGroup: context.isGroup,\n inboundMetadata: context.metadata,\n },\n mergedUserText,\n { skipResetCommands: resetTriggeredAtInit },\n );\n if (slash.matched) {\n ranSlashCommand = true;\n slashCommandMetadata = slash.metadata;\n const text = slash.aggregatedText.trim();\n if (text) {\n webchatSlashReceipt = text;\n pushAssistantReceipt(queue, text, input.runId);\n } else if (channel === 'webchat') {\n webchatSlashReceipt =\n 'Command finished with no assistant text. If you used `/goal`, a follow-up turn may still be scheduled automatically.';\n pushAssistantReceipt(queue, webchatSlashReceipt, input.runId);\n }\n const workflowRun = slash.metadata?.workflowRun;\n if (workflowRun && typeof workflowRun === 'object') {\n queue.push({\n type: 'workflow_run_started',\n runId: input.runId,\n payload: { workflowRun },\n });\n }\n return;\n }\n\n const textForAgent = mergedUserText.trimStart().startsWith('/skill:')\n ? deps.agentManager.expandSkillUserText(mergedUserText)\n : mergedUserText;\n const userMessage = await deps.buildTranscriptUserMessage(textForAgent, prepared, sessionKey);\n let sourceEnrichedUserMessage: AgentMessage = userMessage;\n let sourceImages: ImageContent[] | undefined;\n if (deps.sourceContextResolver) {\n const metadata = await deps.sessionStore.getMetadata(sessionKey).catch(() => null);\n const sourceBinding = metadata?.customData && typeof metadata.customData === 'object'\n ? (metadata.customData as Record<string, unknown>).sourceBinding\n : undefined;\n if (isSessionSourceBinding(sourceBinding)) {\n const sourceContext = await deps.sourceContextResolver(sourceBinding, sessionKey);\n sourceEnrichedUserMessage = injectSourceContextIntoUserMessage(userMessage, sourceContext);\n sourceImages = sourceContext?.images;\n }\n }\n\n if (channel === 'webchat') {\n pushVisible({\n type: 'user_message',\n timestamp: userMessage.timestamp ?? Date.now(),\n content: readAgentMessageContent(userMessage),\n media: userMessage.media,\n });\n if (textForAgent.trim()) {\n deps.enqueueProvisionalSessionTitle?.(sessionKey, textForAgent);\n }\n }\n\n const pendingUserMessage = userMessage as TranscriptUserMessage;\n setPendingTranscriptUserMessage(sessionKey, pendingUserMessage);\n\n try {\n const result = await runDirectAgentTurn(\n {\n sessionStore: deps.sessionStore,\n agentManager: deps.agentManager,\n modelManager: deps.modelManager,\n config: deps.getConfig(),\n },\n {\n sessionKey,\n userMessage: sourceEnrichedUserMessage,\n abortSignal: signal,\n sourceImages,\n runId: input.runId,\n onEvent: (embeddedEvent) => {\n const event = { ...embeddedEvent };\n if (event.type === 'error' && typeof event.content === 'string') {\n event.content = formatStreamError(event.content);\n }\n pushVisible(event);\n },\n },\n );\n\n if (result.lastAssistantText) {\n deps.onTurnComplete?.(sessionKey, result.lastAssistantText);\n }\n if (!result.ok && result.errorMessage && !abortHandled) {\n pushVisible({ type: 'error', content: formatStreamError(result.errorMessage) });\n }\n } finally {\n clearPendingTranscriptUserMessage(sessionKey, pendingUserMessage);\n }\n } catch (err) {\n if (!abortHandled) {\n const em = err instanceof Error ? err.message : String(err);\n pushVisible({ type: 'error', content: formatStreamError(em) });\n }\n } finally {\n queue.close();\n }\n })();\n\n try {\n for await (const event of queue) {\n yield event;\n }\n await taskPromise;\n\n if (channel === 'webchat' && ranSlashCommand) {\n try {\n const userMsg = {\n role: 'user' as const,\n content: [{ type: 'text' as const, text: mergedUserText }],\n timestamp: Date.now(),\n } as AgentMessage;\n await deps.sessionStore.appendTranscriptMessage(sessionKey, userMsg);\n if (webchatSlashReceipt?.trim()) {\n const assistantMsg = {\n role: 'assistant' as const,\n content: [{ type: 'text' as const, text: webchatSlashReceipt.trim() }],\n timestamp: Date.now(),\n ...(slashCommandMetadata && Object.keys(slashCommandMetadata).length > 0\n ? { metadata: slashCommandMetadata }\n : {}),\n } as AgentMessage;\n await deps.sessionStore.appendTranscriptMessage(sessionKey, assistantMsg);\n }\n deps.reloadWebchatTranscript?.(sessionKey);\n } catch (err) {\n deps.log.warn({ err, sessionKey }, 'Failed to persist webchat slash command receipt');\n }\n }\n\n if (!userAborted) {\n const ttsAudioEvent = await deps.maybeEmitWebchatTts(sessionKey, inboundVoice);\n if (ttsAudioEvent) {\n yield ttsAudioEvent;\n }\n }\n\n deps.recordPersistentGoalStreamOutcome?.(sessionKey, { skipPersistentGoalPostTurn: ranSlashCommand });\n } finally {\n if (channel === 'webchat') {\n deps.unregisterWebchatSsePublisher(sessionKey);\n }\n deps.endDirectRequestContext();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"process-direct-streaming.js","names":[],"sources":["../../../../src/agent/service/process-direct-streaming.ts"],"sourcesContent":["import type { AgentMessage } from '@earendil-works/pi-agent-core';\nimport type { ImageContent } from '@earendil-works/pi-ai';\n\nimport type { Config } from '../../config/schema.js';\nimport type { InboundAttachmentInput, MediaRef } from '../../channels/attachments/inbound-persist.js';\nimport { readAgentMessageContent } from '../memory/agent-message-access.js';\nimport {\n isVoiceLikeAttachment,\n mergeVoiceTranscriptsIntoUserText,\n mergeSttConfigFromAppConfig,\n} from '../../channels/attachments/voice-stt-webchat.js';\nimport {\n resolveEffectiveReasoningLevel,\n initSessionTurn,\n type SessionConfigStore,\n type SessionStore,\n} from '../../session/index.js';\nimport type { SessionContext } from '../session/index.js';\nimport { applyReasoningVisibilityToSseEvent } from '../streaming/reasoning-visibility-sse.js';\nimport type { ReasoningLevel } from '../transcript/thinking-types.js';\nimport { formatAgentRunErrorForClient } from '../client-error-format.js';\nimport { abortEmbeddedRun } from '../embedded/runs.js';\nimport type { AgentInstanceGateway } from '../agent-instance-gateway.js';\nimport type { CommandHandler } from '../messaging/command-handler.js';\nimport type { ModelManager } from '../models/index.js';\nimport { injectSourceContextIntoUserMessage } from '../source-context/injector.js';\nimport { isSessionSourceBinding, type AgentSourceContextResolver } from '../source-context/types.js';\n\nimport { AsyncQueue } from './async-queue.js';\nimport {\n hydratePerTurnState,\n runDirectAgentTurn,\n tryRunSlashCommand,\n} from './direct-turn-helpers.js';\nimport {\n clearPendingTranscriptUserMessage,\n setPendingTranscriptUserMessage,\n type TranscriptUserMessage,\n} from '../inbound/attachment-pipeline.js';\n\nexport type DirectStreamInboundAttachment = InboundAttachmentInput;\n\nexport type ProcessDirectStreamLog = {\n info: (obj: Record<string, unknown>, msg: string) => void;\n warn: (obj: Record<string, unknown>, msg: string) => void;\n debug?: (obj: Record<string, unknown>, msg: string) => void;\n};\n\nexport interface ProcessDirectStreamingDeps {\n log: ProcessDirectStreamLog;\n resolveSessionEndpoint: (sessionKey: string) => Promise<{ channel: string; chatId: string }>;\n initDirectStreamingSession: (\n sessionKey: string,\n channel: string,\n chatId: string,\n ) => SessionContext;\n registerWebchatSsePublisher: (\n sessionKey: string,\n publisher: (event: { type: string; [key: string]: unknown }) => void,\n ) => void;\n unregisterWebchatSsePublisher: (sessionKey: string) => void;\n agentManager: AgentInstanceGateway;\n hydrateSessionWorkspaceFromStore: (sessionKey: string) => Promise<void>;\n hydrateSessionModelFromStore: (sessionKey: string) => Promise<void>;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n applyResolvedThinkingLevel: (sessionKey: string, thinking?: string | null) => Promise<void>;\n getConfig: () => Config | undefined;\n sessionConfigStore: SessionConfigStore;\n commandHandler: Pick<CommandHandler, 'executeCommandAndAggregateReply'>;\n prepareInboundAttachments: (\n sessionKey: string,\n attachments?: DirectStreamInboundAttachment[],\n ) => Promise<MediaRef[] | undefined>;\n buildTranscriptUserMessage: (\n content: string,\n prepared: MediaRef[] | undefined,\n sessionKey: string,\n ) => Promise<TranscriptUserMessage>;\n recordPersistentGoalStreamOutcome?: (\n sessionKey: string,\n outcome: { skipPersistentGoalPostTurn: boolean },\n ) => void;\n onTurnComplete?: (sessionKey: string, lastAssistantText?: string) => void;\n enqueueProvisionalSessionTitle?: (sessionKey: string, userText: string) => void;\n /** Disk-only transcript sync (slash receipt already streamed as tokens). */\n reloadWebchatTranscript?: (sessionKey: string) => void;\n maybeEmitWebchatTts: (\n sessionKey: string,\n hadInboundVoice: boolean,\n ) => Promise<{ type: 'tts_audio'; uri: string; mimeType: string; name: string } | null>;\n endDirectRequestContext: () => void;\n resetSession: (sessionKey: string) => Promise<{ sessionId: string; previousSessionId: string } | null>;\n sourceContextResolver?: AgentSourceContextResolver;\n}\n\nexport interface ProcessDirectStreamingInput {\n content: string;\n sessionKey?: string;\n attachments?: DirectStreamInboundAttachment[];\n thinking?: string;\n signal?: AbortSignal;\n runId?: string;\n}\n\nexport type ProcessDirectStreamingSseEvent = { type: string; [key: string]: unknown };\n\nfunction makeAssistantReceiptMessage(text: string): AgentMessage {\n return {\n role: 'assistant',\n content: [{ type: 'text', text }],\n timestamp: Date.now(),\n } as AgentMessage;\n}\n\nfunction pushAssistantReceipt(\n queue: AsyncQueue<ProcessDirectStreamingSseEvent>,\n text: string,\n runId?: string,\n): void {\n const message = makeAssistantReceiptMessage(text);\n queue.push({ type: 'message_start', runId, message });\n queue.push({ type: 'message_update', runId, message });\n queue.push({ type: 'message_end', runId, message });\n}\n\nexport async function* runProcessDirectStreaming(\n deps: ProcessDirectStreamingDeps,\n input: ProcessDirectStreamingInput,\n): AsyncGenerator<ProcessDirectStreamingSseEvent, void, unknown> {\n const sessionKey = input.sessionKey ?? 'agent:main:main';\n const { channel, chatId } = await deps.resolveSessionEndpoint(sessionKey);\n const context = deps.initDirectStreamingSession(sessionKey, channel, chatId);\n\n const queue = new AsyncQueue<ProcessDirectStreamingSseEvent>();\n let reasoningLevel: ReasoningLevel = 'stream';\n\n const pushVisible = (event: ProcessDirectStreamingSseEvent) => {\n const visible = applyReasoningVisibilityToSseEvent(event, reasoningLevel);\n if (visible !== null) {\n queue.push(visible);\n }\n };\n\n const formatStreamError = (raw: string): string => {\n let provider: string | undefined;\n let modelRef: string | undefined;\n try {\n const resolved = deps.modelManager.getResolvedModelForSession(sessionKey);\n provider = resolved.provider;\n modelRef = deps.modelManager.getModelForSession(sessionKey);\n } catch {\n /* ignore — format without provider context */\n }\n return formatAgentRunErrorForClient(raw, { provider, modelRef });\n };\n\n if (channel === 'webchat') {\n deps.registerWebchatSsePublisher(sessionKey, pushVisible);\n }\n\n const signal = input.signal;\n let userAborted = false;\n let abortHandled = false;\n let inboundVoice = false;\n let ranSlashCommand = false;\n let mergedUserText = input.content;\n let webchatSlashReceipt: string | undefined;\n let slashCommandMetadata: Record<string, unknown> | undefined;\n\n const taskPromise = (async () => {\n try {\n const cfg = deps.getConfig();\n let turnBody = input.content;\n let resetTriggeredAtInit = false;\n if (cfg) {\n const turn = await initSessionTurn({\n cfg,\n sessionKey,\n body: input.content,\n resetSession: deps.resetSession,\n });\n resetTriggeredAtInit = turn.resetTriggered;\n if (turn.bareReset && turn.ackMessage) {\n ranSlashCommand = true;\n webchatSlashReceipt = turn.ackMessage;\n pushAssistantReceipt(queue, turn.ackMessage, input.runId);\n return;\n }\n turnBody = turn.bodyStripped;\n if (turn.isNewSession) {\n deps.log.debug(\n {\n sessionKey,\n sessionId: turn.sessionId,\n previousSessionId: turn.previousSessionId,\n resetTriggered: turn.resetTriggered,\n staleRollover: turn.staleRollover,\n },\n 'Session reset boundary at direct turn start',\n );\n }\n }\n\n await hydratePerTurnState(deps, sessionKey, input.thinking);\n {\n const defReason = (deps.getConfig()?.agents?.defaults?.reasoningDefault ?? 'stream') as ReasoningLevel;\n reasoningLevel = await resolveEffectiveReasoningLevel(deps.sessionConfigStore, sessionKey, defReason);\n }\n\n const prepared = await deps.prepareInboundAttachments(sessionKey, input.attachments);\n\n const sttCfg = mergeSttConfigFromAppConfig(deps.getConfig()?.tools?.media?.audio, deps.getConfig()?.tools?.media);\n const voiceMerge = await mergeVoiceTranscriptsIntoUserText(prepared, turnBody, sttCfg);\n mergedUserText = voiceMerge.text;\n inboundVoice = voiceMerge.inboundVoice;\n\n if (inboundVoice) {\n const transcriptParts = [\n voiceMerge.voiceTranscripts.filter(Boolean).join('\\n'),\n turnBody.trim(),\n ].filter(Boolean);\n const voiceMedia = (prepared ?? []).filter(isVoiceLikeAttachment).map((att) => ({\n uri: att.uri,\n mimeType: att.mimeType,\n name: att.name,\n }));\n pushVisible({\n type: 'user_transcript',\n text: transcriptParts.join('\\n\\n'),\n media: voiceMedia,\n });\n }\n\n const armAbort = () => {\n if (abortHandled) {\n return;\n }\n abortHandled = true;\n userAborted = true;\n void abortEmbeddedRun(sessionKey);\n queue.close();\n };\n if (signal) {\n if (signal.aborted) {\n armAbort();\n return;\n }\n signal.addEventListener('abort', armAbort, { once: true });\n }\n\n const slash = await tryRunSlashCommand(\n deps,\n {\n sessionKey,\n channel,\n chatId,\n senderId: context.senderId,\n isGroup: context.isGroup,\n inboundMetadata: context.metadata,\n },\n mergedUserText,\n { skipResetCommands: resetTriggeredAtInit },\n );\n if (slash.matched) {\n ranSlashCommand = true;\n slashCommandMetadata = slash.metadata;\n const text = slash.aggregatedText.trim();\n if (text) {\n webchatSlashReceipt = text;\n pushAssistantReceipt(queue, text, input.runId);\n } else if (channel === 'webchat') {\n webchatSlashReceipt =\n 'Command finished with no assistant text. If you used `/goal`, a follow-up turn may still be scheduled automatically.';\n pushAssistantReceipt(queue, webchatSlashReceipt, input.runId);\n }\n const workflowRun = slash.metadata?.workflowRun;\n if (workflowRun && typeof workflowRun === 'object') {\n queue.push({\n type: 'workflow_run_started',\n runId: input.runId,\n payload: { workflowRun },\n });\n }\n return;\n }\n\n const textForAgent = mergedUserText.trimStart().startsWith('/skill:')\n ? deps.agentManager.expandSkillUserText(mergedUserText)\n : mergedUserText;\n const userMessage = await deps.buildTranscriptUserMessage(textForAgent, prepared, sessionKey);\n let sourceEnrichedUserMessage: AgentMessage = userMessage;\n let sourceImages: ImageContent[] | undefined;\n if (deps.sourceContextResolver) {\n const metadata = await deps.sessionStore.getMetadata(sessionKey).catch(() => null);\n const sourceBinding = metadata?.customData && typeof metadata.customData === 'object'\n ? (metadata.customData as Record<string, unknown>).sourceBinding\n : undefined;\n if (isSessionSourceBinding(sourceBinding)) {\n const sourceContext = await deps.sourceContextResolver(sourceBinding, sessionKey);\n sourceEnrichedUserMessage = injectSourceContextIntoUserMessage(userMessage, sourceContext);\n sourceImages = sourceContext?.images;\n }\n }\n\n if (channel === 'webchat') {\n pushVisible({\n type: 'user_message',\n timestamp: userMessage.timestamp ?? Date.now(),\n content: readAgentMessageContent(userMessage),\n media: userMessage.media,\n });\n if (textForAgent.trim()) {\n deps.enqueueProvisionalSessionTitle?.(sessionKey, textForAgent);\n }\n }\n\n const pendingUserMessage = userMessage as TranscriptUserMessage;\n setPendingTranscriptUserMessage(sessionKey, pendingUserMessage);\n\n try {\n const result = await runDirectAgentTurn(\n {\n sessionStore: deps.sessionStore,\n agentManager: deps.agentManager,\n modelManager: deps.modelManager,\n config: deps.getConfig(),\n },\n {\n sessionKey,\n userMessage: sourceEnrichedUserMessage,\n abortSignal: signal,\n sourceImages,\n runId: input.runId,\n onEvent: (embeddedEvent) => {\n const event = { ...embeddedEvent };\n if (event.type === 'error' && typeof event.content === 'string') {\n event.content = formatStreamError(event.content);\n }\n pushVisible(event);\n },\n },\n );\n\n if (result.lastAssistantText) {\n deps.onTurnComplete?.(sessionKey, result.lastAssistantText);\n }\n if (!result.ok && result.errorMessage && !abortHandled) {\n pushVisible({ type: 'error', content: formatStreamError(result.errorMessage) });\n }\n } finally {\n clearPendingTranscriptUserMessage(sessionKey, pendingUserMessage);\n }\n } catch (err) {\n if (!abortHandled) {\n const em = err instanceof Error ? err.message : String(err);\n pushVisible({ type: 'error', content: formatStreamError(em) });\n }\n } finally {\n if (!userAborted && channel === 'webchat') {\n try {\n const ttsAudioEvent = await deps.maybeEmitWebchatTts(sessionKey, inboundVoice);\n if (ttsAudioEvent) {\n queue.push(ttsAudioEvent);\n }\n } catch (ttsErr) {\n deps.log.warn({ err: ttsErr, sessionKey }, 'Failed to emit TTS audio before stream close');\n }\n }\n queue.close();\n }\n })();\n\n try {\n for await (const event of queue) {\n yield event;\n }\n await taskPromise;\n\n if (channel === 'webchat' && ranSlashCommand) {\n try {\n const userMsg = {\n role: 'user' as const,\n content: [{ type: 'text' as const, text: mergedUserText }],\n timestamp: Date.now(),\n } as AgentMessage;\n await deps.sessionStore.appendTranscriptMessage(sessionKey, userMsg);\n if (webchatSlashReceipt?.trim()) {\n const assistantMsg = {\n role: 'assistant' as const,\n content: [{ type: 'text' as const, text: webchatSlashReceipt.trim() }],\n timestamp: Date.now(),\n ...(slashCommandMetadata && Object.keys(slashCommandMetadata).length > 0\n ? { metadata: slashCommandMetadata }\n : {}),\n } as AgentMessage;\n await deps.sessionStore.appendTranscriptMessage(sessionKey, assistantMsg);\n }\n deps.reloadWebchatTranscript?.(sessionKey);\n } catch (err) {\n deps.log.warn({ err, sessionKey }, 'Failed to persist webchat slash command receipt');\n }\n }\n\n deps.recordPersistentGoalStreamOutcome?.(sessionKey, { skipPersistentGoalPostTurn: ranSlashCommand });\n } finally {\n if (channel === 'webchat') {\n deps.unregisterWebchatSsePublisher(sessionKey);\n }\n deps.endDirectRequestContext();\n }\n}\n"],"mappings":"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"}
@@ -148,7 +148,7 @@ export declare class AgentService {
148
148
  */
149
149
  private enqueueMaybeAutoTitleAfterPersist;
150
150
  private prepareLoadedSessionMessages;
151
- private parseSessionKey;
151
+ private resolveSessionEndpoint;
152
152
  private initSessionContext;
153
153
  /**
154
154
  * Persist inbound file attachments to the global media store.
@@ -57,7 +57,6 @@ import { FeedbackCoordinator } from "./feedback/feedback-coordinator.js";
57
57
  import "./feedback/index.js";
58
58
  import { PersistentGoalService } from "./goals/persistent-goal-service.js";
59
59
  import { reconcileManagedDreamingCronJobs } from "./service/reconcile-dreaming-cron.js";
60
- import { parseOutboundSessionKey } from "./service/parse-outbound-session-key.js";
61
60
  import { cleanTrailingErrors } from "./memory/message-sanitizer.js";
62
61
  import { MemoryFlushService } from "./memory/memory-flush.js";
63
62
  import { tryApplySessionTranscriptHygiene } from "./transcript/transcript-hygiene.js";
@@ -232,7 +231,8 @@ var AgentService = class {
232
231
  gatewayClarify: config.gatewayClarify,
233
232
  getCronService: config.getCronService,
234
233
  getWorkflowRunService: config.getWorkflowRunService,
235
- onSkillsUpdated: config.onSkillsUpdated
234
+ onSkillsUpdated: config.onSkillsUpdated,
235
+ isWorkspaceTrusted: config.isWorkspaceTrusted
236
236
  });
237
237
  this.agentEventHandler = new AgentEventHandler({
238
238
  progressManager: this.progressManager,
@@ -356,7 +356,7 @@ var AgentService = class {
356
356
  if (!c) throw new Error("AgentService requires config.config");
357
357
  return c;
358
358
  },
359
- parseSessionKey: (sk) => this.parseSessionKey(sk),
359
+ resolveSessionEndpoint: (sk) => this.resolveSessionEndpoint(sk),
360
360
  initSessionContext: (sk, channel, chatId) => this.initSessionContext(sk, channel, chatId),
361
361
  sessionHydrator: this.sessionHydrator,
362
362
  prepareInboundAttachments: (sk, att) => this.prepareInboundAttachments(sk, att),
@@ -662,8 +662,12 @@ var AgentService = class {
662
662
  }
663
663
  return out;
664
664
  }
665
- parseSessionKey(sessionKey) {
666
- return parseOutboundSessionKey(sessionKey, this.config.config);
665
+ async resolveSessionEndpoint(sessionKey) {
666
+ const metadata = await this.sessionStore.getMetadata(sessionKey).catch(() => null);
667
+ return {
668
+ channel: metadata?.routing?.source?.trim() || metadata?.sourceChannel?.trim() || "cli",
669
+ chatId: metadata?.routing?.peerId?.trim() || metadata?.sourceChatId?.trim() || "main"
670
+ };
667
671
  }
668
672
  initSessionContext(sessionKey, channel, chatId, senderId = "") {
669
673
  const context = {