@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":"persistent-goal-service.js","names":["parseRoutingSessionKey"],"sources":["../../../../src/agent/goals/persistent-goal-service.ts"],"sourcesContent":["/**\n * PersistentGoalService — owns the \"/goal\" runtime: continuation scheduling,\n * the `PersistentGoalApis` bag that command handlers receive, and the post-turn\n * verdict hook called from `OutboundCoordinator`.\n *\n * Previously this logic was scattered across `AgentService`:\n * - `setPersistentGoalWebchatContinuationScheduler` + a private callback field\n * - `schedulePersistentGoalContinuation` (bus vs webchat fork)\n * - `getPersistentGoalApisForCommand` (~40-line API factory)\n * - `recordPersistentGoalStreamOutcome` / `takePersistentGoalStreamOutcome`\n * - the `/goal` half of `emitSessionTurnComplete` (delegated to\n * `handlePersistentGoalPostTurn`)\n *\n * Concentrating it here gives the rest of `AgentService` a cleaner surface\n * (one collaborator instead of five methods) and makes the goal runtime\n * unit-testable in isolation.\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { Config } from '../../config/schema.js';\nimport type { MessageBus } from '../../infra/bus/index.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { SessionStore } from '../../session/store.js';\nimport type { SessionStateBag } from '../session/index.js';\nimport type { GoalWithDetails } from '../../goals/types.js';\nimport { parseSessionKey as parseRoutingSessionKey } from '../../routing/session-key.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { PersistentGoalApis } from './persistent-goal-apis.js';\nimport { handlePersistentGoalPostTurn } from './post-turn.js';\n\nconst log = createLogger('PersistentGoalService');\n\nexport interface PersistentGoalRouting {\n sessionKey: string;\n channel: string;\n chatId: string;\n inboundMetadata?: Record<string, unknown>;\n}\n\nexport interface SessionTurnCompletionForGoal {\n sessionKey: string;\n channel: string;\n chatId: string;\n assistantPlainText: string;\n aborted: boolean;\n streamError?: string;\n skipPersistentGoalPostTurn?: boolean;\n outboundMetadata?: Record<string, unknown>;\n}\n\nexport interface PersistentGoalServiceOptions {\n bus: MessageBus;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n sessionState: SessionStateBag;\n /** Effective config snapshot accessor. */\n getConfig: () => Config | undefined;\n /** Resolve the workspace directory for `appendAssistantReceipt` writes. */\n getResolvedWorkspaceForSession: (sessionKey: string) => string;\n /** Notify the gateway UI after a metadata change (replaces the in-bag emit). */\n onSessionMetadataUpdated?: (sessionKey: string) => void;\n /** Push an assistant token + transcript refresh into a live webchat stream. */\n notifyWebchatTranscriptAppend: (sessionKey: string, assistantText: string) => void;\n /** Gateway: durable goal status changed after post-turn judge. */\n onGoalStatusUpdated?: (payload: {\n goalId: string;\n sessionKey: string;\n previousStatus: string;\n status: string;\n goal: GoalWithDetails;\n }) => void;\n}\n\nexport class PersistentGoalService {\n private readonly opts: PersistentGoalServiceOptions;\n /** Gateway only: webchat continuations bypass the bus and reuse `runGatewayAgent`. */\n private webchatContinuationScheduler?: (sessionKey: string, message: string) => void;\n\n constructor(opts: PersistentGoalServiceOptions) {\n this.opts = opts;\n }\n\n /** Register the gateway-side webchat continuation hook (set from `web-agent` wiring). */\n setWebchatContinuationScheduler(\n fn: ((sessionKey: string, message: string) => void) | undefined,\n ): void {\n this.webchatContinuationScheduler = fn;\n }\n\n /**\n * Continue a session after `/goal` decides the previous turn needs follow-up.\n * Webchat sessions go through the scheduler; bus-driven channels re-publish the\n * follow-up message as an inbound bus event so the existing inbound loop picks it up.\n */\n scheduleContinuation(\n sessionKey: string,\n message: string,\n routing: { channel: string; chatId: string; inboundMetadata?: Record<string, unknown> },\n ): void {\n const parsed = parseRoutingSessionKey(sessionKey);\n if (parsed?.source === 'webchat' && this.webchatContinuationScheduler) {\n this.webchatContinuationScheduler(sessionKey, message);\n return;\n }\n queueMicrotask(() => {\n void this.opts.bus\n .publishInbound({\n channel: routing.channel,\n chat_id: routing.chatId,\n sender_id: 'persistent-goal',\n content: message,\n metadata: { sessionKey, ...routing.inboundMetadata },\n })\n .catch((err) => {\n log.warn({ err, sessionKey }, 'Persistent goal: publishInbound failed');\n });\n });\n }\n\n /** Build the per-command `PersistentGoalApis` bag (transcript writers + scheduler closures). */\n buildApisForRouting(routing: PersistentGoalRouting): PersistentGoalApis {\n return {\n getSessionMetadata: (k) => this.opts.sessionStore.getMetadata(k),\n updateSessionMetadata: async (k, u) => {\n await this.opts.sessionStore.updateMetadata(k, u);\n this.opts.onSessionMetadataUpdated?.(k);\n },\n loadMessages: (k) => this.opts.sessionStore.loadMessages(k),\n appendAssistantReceipt: async (k, text) => {\n const trimmed = text.trim();\n if (!trimmed) return;\n await this.opts.sessionStore.appendTranscriptMessage(k, {\n role: 'assistant',\n content: [{ type: 'text', text: trimmed }],\n timestamp: Date.now(),\n } as AgentMessage);\n this.opts.notifyWebchatTranscriptAppend(k, trimmed);\n },\n scheduleContinuation: (sk, msg) => {\n this.scheduleContinuation(sk, msg, {\n channel: routing.channel,\n chatId: routing.chatId,\n inboundMetadata: routing.inboundMetadata,\n });\n },\n inboundConcurrentDepth: (sk) => this.opts.sessionState.getInboundTurnDepth(sk),\n };\n }\n\n recordStreamOutcome(sessionKey: string, outcome: { skipPersistentGoalPostTurn: boolean }): void {\n this.opts.sessionState.recordPersistentGoalStreamOutcome(sessionKey, outcome);\n }\n\n takeStreamOutcome(sessionKey: string): { skipPersistentGoalPostTurn: boolean } | undefined {\n return this.opts.sessionState.takePersistentGoalStreamOutcome(sessionKey);\n }\n\n /**\n * Run the `/goal` post-turn verdict for a completed user turn (called from\n * `OutboundCoordinator.emitSessionTurnComplete`).\n */\n async runPostTurn(payload: SessionTurnCompletionForGoal): Promise<void> {\n const apis = this.buildApisForRouting({\n sessionKey: payload.sessionKey,\n channel: payload.channel,\n chatId: payload.chatId,\n inboundMetadata: payload.outboundMetadata,\n });\n\n const src = parseRoutingSessionKey(payload.sessionKey)?.source;\n const isWebchat = src === 'webchat';\n const publishVerdict =\n !isWebchat && payload.channel !== 'cli'\n ? async (text: string) => {\n await this.opts.bus.publishOutbound({\n channel: payload.channel,\n chat_id: payload.chatId,\n content: text,\n type: 'message',\n metadata: {\n accountId: payload.outboundMetadata?.accountId,\n threadId: payload.outboundMetadata?.threadId,\n },\n });\n }\n : undefined;\n\n let runtimeSessionModelRef: string | undefined;\n try {\n runtimeSessionModelRef = this.opts.modelManager.getModelForSession(payload.sessionKey);\n } catch {\n runtimeSessionModelRef = undefined;\n }\n\n await handlePersistentGoalPostTurn({\n apis,\n sessionKey: payload.sessionKey,\n assistantPlainText: payload.assistantPlainText,\n aborted: payload.aborted,\n ...(payload.streamError !== undefined ? { streamError: payload.streamError } : {}),\n skipPersistentGoalPostTurn: payload.skipPersistentGoalPostTurn ?? false,\n config: this.opts.getConfig(),\n runtimeSessionModelRef,\n publishVerdictToChannel: publishVerdict,\n onGoalStatusUpdated: this.opts.onGoalStatusUpdated,\n });\n }\n}\n"],"mappings":";;;;;kBA0ByF;aACpC;AAIrD,MAAM,MAAM,aAAa,wBAAwB;AA2CjD,IAAa,wBAAb,MAAmC;CACjC;;CAEA;CAEA,YAAY,MAAoC;AAC9C,OAAK,OAAO;;;CAId,gCACE,IACM;AACN,OAAK,+BAA+B;;;;;;;CAQtC,qBACE,YACA,SACA,SACM;AAEN,MADeA,gBAAuB,WAC5B,EAAE,WAAW,aAAa,KAAK,8BAA8B;AACrE,QAAK,6BAA6B,YAAY,QAAQ;AACtD;;AAEF,uBAAqB;AACd,QAAK,KAAK,IACZ,eAAe;IACd,SAAS,QAAQ;IACjB,SAAS,QAAQ;IACjB,WAAW;IACX,SAAS;IACT,UAAU;KAAE;KAAY,GAAG,QAAQ;KAAiB;IACrD,CAAC,CACD,OAAO,QAAQ;AACd,QAAI,KAAK;KAAE;KAAK;KAAY,EAAE,yCAAyC;KACvE;IACJ;;;CAIJ,oBAAoB,SAAoD;AACtE,SAAO;GACL,qBAAqB,MAAM,KAAK,KAAK,aAAa,YAAY,EAAE;GAChE,uBAAuB,OAAO,GAAG,MAAM;AACrC,UAAM,KAAK,KAAK,aAAa,eAAe,GAAG,EAAE;AACjD,SAAK,KAAK,2BAA2B,EAAE;;GAEzC,eAAe,MAAM,KAAK,KAAK,aAAa,aAAa,EAAE;GAC3D,wBAAwB,OAAO,GAAG,SAAS;IACzC,MAAM,UAAU,KAAK,MAAM;AAC3B,QAAI,CAAC,QAAS;AACd,UAAM,KAAK,KAAK,aAAa,wBAAwB,GAAG;KACtD,MAAM;KACN,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM;MAAS,CAAC;KAC1C,WAAW,KAAK,KAAK;KACtB,CAAiB;AAClB,SAAK,KAAK,8BAA8B,GAAG,QAAQ;;GAErD,uBAAuB,IAAI,QAAQ;AACjC,SAAK,qBAAqB,IAAI,KAAK;KACjC,SAAS,QAAQ;KACjB,QAAQ,QAAQ;KAChB,iBAAiB,QAAQ;KAC1B,CAAC;;GAEJ,yBAAyB,OAAO,KAAK,KAAK,aAAa,oBAAoB,GAAG;GAC/E;;CAGH,oBAAoB,YAAoB,SAAwD;AAC9F,OAAK,KAAK,aAAa,kCAAkC,YAAY,QAAQ;;CAG/E,kBAAkB,YAAyE;AACzF,SAAO,KAAK,KAAK,aAAa,gCAAgC,WAAW;;;;;;CAO3E,MAAM,YAAY,SAAsD;EACtE,MAAM,OAAO,KAAK,oBAAoB;GACpC,YAAY,QAAQ;GACpB,SAAS,QAAQ;GACjB,QAAQ,QAAQ;GAChB,iBAAiB,QAAQ;GAC1B,CAAC;EAIF,MAAM,iBACJ,EAHUA,gBAAuB,QAAQ,WAAW,EAAE,WAC9B,cAEV,QAAQ,YAAY,QAC9B,OAAO,SAAiB;AACtB,SAAM,KAAK,KAAK,IAAI,gBAAgB;IAClC,SAAS,QAAQ;IACjB,SAAS,QAAQ;IACjB,SAAS;IACT,MAAM;IACN,UAAU;KACR,WAAW,QAAQ,kBAAkB;KACrC,UAAU,QAAQ,kBAAkB;KACrC;IACF,CAAC;MAEJ,KAAA;EAEN,IAAI;AACJ,MAAI;AACF,4BAAyB,KAAK,KAAK,aAAa,mBAAmB,QAAQ,WAAW;UAChF;AACN,4BAAyB,KAAA;;AAG3B,QAAM,6BAA6B;GACjC;GACA,YAAY,QAAQ;GACpB,oBAAoB,QAAQ;GAC5B,SAAS,QAAQ;GACjB,GAAI,QAAQ,gBAAgB,KAAA,IAAY,EAAE,aAAa,QAAQ,aAAa,GAAG,EAAE;GACjF,4BAA4B,QAAQ,8BAA8B;GAClE,QAAQ,KAAK,KAAK,WAAW;GAC7B;GACA,yBAAyB;GACzB,qBAAqB,KAAK,KAAK;GAChC,CAAC"}
1
+ {"version":3,"file":"persistent-goal-service.js","names":[],"sources":["../../../../src/agent/goals/persistent-goal-service.ts"],"sourcesContent":["/**\n * PersistentGoalService — owns the \"/goal\" runtime: continuation scheduling,\n * the `PersistentGoalApis` bag that command handlers receive, and the post-turn\n * verdict hook called from `OutboundCoordinator`.\n *\n * Previously this logic was scattered across `AgentService`:\n * - `setPersistentGoalWebchatContinuationScheduler` + a private callback field\n * - `schedulePersistentGoalContinuation` (bus vs webchat fork)\n * - `getPersistentGoalApisForCommand` (~40-line API factory)\n * - `recordPersistentGoalStreamOutcome` / `takePersistentGoalStreamOutcome`\n * - the `/goal` half of `emitSessionTurnComplete` (delegated to\n * `handlePersistentGoalPostTurn`)\n *\n * Concentrating it here gives the rest of `AgentService` a cleaner surface\n * (one collaborator instead of five methods) and makes the goal runtime\n * unit-testable in isolation.\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { Config } from '../../config/schema.js';\nimport type { MessageBus } from '../../infra/bus/index.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { SessionStore } from '../../session/store.js';\nimport type { SessionStateBag } from '../session/index.js';\nimport type { GoalWithDetails } from '../../goals/types.js';\nimport { createLogger } from '../../utils/logger.js';\nimport type { PersistentGoalApis } from './persistent-goal-apis.js';\nimport { handlePersistentGoalPostTurn } from './post-turn.js';\n\nconst log = createLogger('PersistentGoalService');\n\nexport interface PersistentGoalRouting {\n sessionKey: string;\n channel: string;\n chatId: string;\n inboundMetadata?: Record<string, unknown>;\n}\n\nexport interface SessionTurnCompletionForGoal {\n sessionKey: string;\n channel: string;\n chatId: string;\n assistantPlainText: string;\n aborted: boolean;\n streamError?: string;\n skipPersistentGoalPostTurn?: boolean;\n outboundMetadata?: Record<string, unknown>;\n}\n\nexport interface PersistentGoalServiceOptions {\n bus: MessageBus;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n sessionState: SessionStateBag;\n /** Effective config snapshot accessor. */\n getConfig: () => Config | undefined;\n /** Resolve the workspace directory for `appendAssistantReceipt` writes. */\n getResolvedWorkspaceForSession: (sessionKey: string) => string;\n /** Notify the gateway UI after a metadata change (replaces the in-bag emit). */\n onSessionMetadataUpdated?: (sessionKey: string) => void;\n /** Push an assistant token + transcript refresh into a live webchat stream. */\n notifyWebchatTranscriptAppend: (sessionKey: string, assistantText: string) => void;\n /** Gateway: durable goal status changed after post-turn judge. */\n onGoalStatusUpdated?: (payload: {\n goalId: string;\n sessionKey: string;\n previousStatus: string;\n status: string;\n goal: GoalWithDetails;\n }) => void;\n}\n\nexport class PersistentGoalService {\n private readonly opts: PersistentGoalServiceOptions;\n /** Gateway only: webchat continuations bypass the bus and reuse `runGatewayAgent`. */\n private webchatContinuationScheduler?: (sessionKey: string, message: string) => void;\n\n constructor(opts: PersistentGoalServiceOptions) {\n this.opts = opts;\n }\n\n /** Register the gateway-side webchat continuation hook (set from `web-agent` wiring). */\n setWebchatContinuationScheduler(\n fn: ((sessionKey: string, message: string) => void) | undefined,\n ): void {\n this.webchatContinuationScheduler = fn;\n }\n\n /**\n * Continue a session after `/goal` decides the previous turn needs follow-up.\n * Webchat sessions go through the scheduler; bus-driven channels re-publish the\n * follow-up message as an inbound bus event so the existing inbound loop picks it up.\n */\n scheduleContinuation(\n sessionKey: string,\n message: string,\n routing: { channel: string; chatId: string; inboundMetadata?: Record<string, unknown> },\n ): void {\n if (routing.channel === 'webchat' && this.webchatContinuationScheduler) {\n this.webchatContinuationScheduler(sessionKey, message);\n return;\n }\n queueMicrotask(() => {\n void this.opts.bus\n .publishInbound({\n channel: routing.channel,\n chat_id: routing.chatId,\n sender_id: 'persistent-goal',\n content: message,\n metadata: { sessionKey, ...routing.inboundMetadata },\n })\n .catch((err) => {\n log.warn({ err, sessionKey }, 'Persistent goal: publishInbound failed');\n });\n });\n }\n\n /** Build the per-command `PersistentGoalApis` bag (transcript writers + scheduler closures). */\n buildApisForRouting(routing: PersistentGoalRouting): PersistentGoalApis {\n return {\n getSessionMetadata: (k) => this.opts.sessionStore.getMetadata(k),\n updateSessionMetadata: async (k, u) => {\n await this.opts.sessionStore.updateMetadata(k, u);\n this.opts.onSessionMetadataUpdated?.(k);\n },\n loadMessages: (k) => this.opts.sessionStore.loadMessages(k),\n appendAssistantReceipt: async (k, text) => {\n const trimmed = text.trim();\n if (!trimmed) return;\n await this.opts.sessionStore.appendTranscriptMessage(k, {\n role: 'assistant',\n content: [{ type: 'text', text: trimmed }],\n timestamp: Date.now(),\n } as AgentMessage);\n this.opts.notifyWebchatTranscriptAppend(k, trimmed);\n },\n scheduleContinuation: (sk, msg) => {\n this.scheduleContinuation(sk, msg, {\n channel: routing.channel,\n chatId: routing.chatId,\n inboundMetadata: routing.inboundMetadata,\n });\n },\n inboundConcurrentDepth: (sk) => this.opts.sessionState.getInboundTurnDepth(sk),\n };\n }\n\n recordStreamOutcome(sessionKey: string, outcome: { skipPersistentGoalPostTurn: boolean }): void {\n this.opts.sessionState.recordPersistentGoalStreamOutcome(sessionKey, outcome);\n }\n\n takeStreamOutcome(sessionKey: string): { skipPersistentGoalPostTurn: boolean } | undefined {\n return this.opts.sessionState.takePersistentGoalStreamOutcome(sessionKey);\n }\n\n /**\n * Run the `/goal` post-turn verdict for a completed user turn (called from\n * `OutboundCoordinator.emitSessionTurnComplete`).\n */\n async runPostTurn(payload: SessionTurnCompletionForGoal): Promise<void> {\n const apis = this.buildApisForRouting({\n sessionKey: payload.sessionKey,\n channel: payload.channel,\n chatId: payload.chatId,\n inboundMetadata: payload.outboundMetadata,\n });\n\n const isWebchat = payload.channel === 'webchat';\n const publishVerdict =\n !isWebchat && payload.channel !== 'cli'\n ? async (text: string) => {\n await this.opts.bus.publishOutbound({\n channel: payload.channel,\n chat_id: payload.chatId,\n content: text,\n type: 'message',\n metadata: {\n accountId: payload.outboundMetadata?.accountId,\n threadId: payload.outboundMetadata?.threadId,\n },\n });\n }\n : undefined;\n\n let runtimeSessionModelRef: string | undefined;\n try {\n runtimeSessionModelRef = this.opts.modelManager.getModelForSession(payload.sessionKey);\n } catch {\n runtimeSessionModelRef = undefined;\n }\n\n await handlePersistentGoalPostTurn({\n apis,\n sessionKey: payload.sessionKey,\n assistantPlainText: payload.assistantPlainText,\n aborted: payload.aborted,\n ...(payload.streamError !== undefined ? { streamError: payload.streamError } : {}),\n skipPersistentGoalPostTurn: payload.skipPersistentGoalPostTurn ?? false,\n config: this.opts.getConfig(),\n runtimeSessionModelRef,\n publishVerdictToChannel: publishVerdict,\n onGoalStatusUpdated: this.opts.onGoalStatusUpdated,\n });\n }\n}\n"],"mappings":";;;;aA0BqD;AAIrD,MAAM,MAAM,aAAa,wBAAwB;AA2CjD,IAAa,wBAAb,MAAmC;CACjC;;CAEA;CAEA,YAAY,MAAoC;AAC9C,OAAK,OAAO;;;CAId,gCACE,IACM;AACN,OAAK,+BAA+B;;;;;;;CAQtC,qBACE,YACA,SACA,SACM;AACN,MAAI,QAAQ,YAAY,aAAa,KAAK,8BAA8B;AACtE,QAAK,6BAA6B,YAAY,QAAQ;AACtD;;AAEF,uBAAqB;AACd,QAAK,KAAK,IACZ,eAAe;IACd,SAAS,QAAQ;IACjB,SAAS,QAAQ;IACjB,WAAW;IACX,SAAS;IACT,UAAU;KAAE;KAAY,GAAG,QAAQ;KAAiB;IACrD,CAAC,CACD,OAAO,QAAQ;AACd,QAAI,KAAK;KAAE;KAAK;KAAY,EAAE,yCAAyC;KACvE;IACJ;;;CAIJ,oBAAoB,SAAoD;AACtE,SAAO;GACL,qBAAqB,MAAM,KAAK,KAAK,aAAa,YAAY,EAAE;GAChE,uBAAuB,OAAO,GAAG,MAAM;AACrC,UAAM,KAAK,KAAK,aAAa,eAAe,GAAG,EAAE;AACjD,SAAK,KAAK,2BAA2B,EAAE;;GAEzC,eAAe,MAAM,KAAK,KAAK,aAAa,aAAa,EAAE;GAC3D,wBAAwB,OAAO,GAAG,SAAS;IACzC,MAAM,UAAU,KAAK,MAAM;AAC3B,QAAI,CAAC,QAAS;AACd,UAAM,KAAK,KAAK,aAAa,wBAAwB,GAAG;KACtD,MAAM;KACN,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM;MAAS,CAAC;KAC1C,WAAW,KAAK,KAAK;KACtB,CAAiB;AAClB,SAAK,KAAK,8BAA8B,GAAG,QAAQ;;GAErD,uBAAuB,IAAI,QAAQ;AACjC,SAAK,qBAAqB,IAAI,KAAK;KACjC,SAAS,QAAQ;KACjB,QAAQ,QAAQ;KAChB,iBAAiB,QAAQ;KAC1B,CAAC;;GAEJ,yBAAyB,OAAO,KAAK,KAAK,aAAa,oBAAoB,GAAG;GAC/E;;CAGH,oBAAoB,YAAoB,SAAwD;AAC9F,OAAK,KAAK,aAAa,kCAAkC,YAAY,QAAQ;;CAG/E,kBAAkB,YAAyE;AACzF,SAAO,KAAK,KAAK,aAAa,gCAAgC,WAAW;;;;;;CAO3E,MAAM,YAAY,SAAsD;EACtE,MAAM,OAAO,KAAK,oBAAoB;GACpC,YAAY,QAAQ;GACpB,SAAS,QAAQ;GACjB,QAAQ,QAAQ;GAChB,iBAAiB,QAAQ;GAC1B,CAAC;EAGF,MAAM,iBACJ,EAFgB,QAAQ,YAAY,cAEtB,QAAQ,YAAY,QAC9B,OAAO,SAAiB;AACtB,SAAM,KAAK,KAAK,IAAI,gBAAgB;IAClC,SAAS,QAAQ;IACjB,SAAS,QAAQ;IACjB,SAAS;IACT,MAAM;IACN,UAAU;KACR,WAAW,QAAQ,kBAAkB;KACrC,UAAU,QAAQ,kBAAkB;KACrC;IACF,CAAC;MAEJ,KAAA;EAEN,IAAI;AACJ,MAAI;AACF,4BAAyB,KAAK,KAAK,aAAa,mBAAmB,QAAQ,WAAW;UAChF;AACN,4BAAyB,KAAA;;AAG3B,QAAM,6BAA6B;GACjC;GACA,YAAY,QAAQ;GACpB,oBAAoB,QAAQ;GAC5B,SAAS,QAAQ;GACjB,GAAI,QAAQ,gBAAgB,KAAA,IAAY,EAAE,aAAa,QAAQ,aAAa,GAAG,EAAE;GACjF,4BAA4B,QAAQ,8BAA8B;GAClE,QAAQ,KAAK,KAAK,WAAW;GAC7B;GACA,yBAAyB;GACzB,qBAAqB,KAAK,KAAK;GAChC,CAAC"}
@@ -85,7 +85,11 @@ var InboundLoop = class {
85
85
  isGroup: context.isGroup || false,
86
86
  metadata: { transcribedVoice: msg.metadata?.transcribedVoice === true }
87
87
  };
88
- updateAsyncLogContext({ sessionId: sessionContext.sessionKey });
88
+ const sessionMetadata = await this.cfg.sessionStore.getMetadata(sessionContext.sessionKey).catch(() => null);
89
+ updateAsyncLogContext({
90
+ sessionKey: sessionContext.sessionKey,
91
+ ...sessionMetadata?.sessionId ? { sessionId: sessionMetadata.sessionId } : {}
92
+ });
89
93
  await this.cfg.sessionContextManager.runWith(sessionContext, async () => {
90
94
  this.cfg.feedbackCoordinator.setContext(sessionContext);
91
95
  this.cfg.agentManager.getOrCreateAgent(sessionContext.sessionKey);
@@ -1 +1 @@
1
- {"version":3,"file":"inbound-loop.js","names":[],"sources":["../../../../src/agent/inbound/inbound-loop.ts"],"sourcesContent":["/**\n * InboundLoop — owns the bus-driven inbound pipeline.\n *\n * Replaces the previous `AgentService.start()` while-loop, `handleInboundMessage`,\n * and `handleSystemMessage` methods. Everything that \"a message arrives, run the\n * agent turn, publish the reply\" needs is concentrated here; the parent\n * `AgentService` now just constructs the loop with collaborators and forwards\n * start/stop to it.\n *\n * The constructor takes a fairly wide config because handling a single inbound\n * message touches almost every subsystem (router, command handler, lifecycle\n * manager, agent orchestrator, outbound coordinator, session state, etc.). All\n * dependencies are injected so this class is unit-testable and the inbound\n * pipeline can evolve without touching `AgentService`.\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { MessageBus, InboundMessage } from '../../infra/bus/index.js';\nimport { MessageBusShutdownError } from '../../infra/bus/index.js';\nimport type { Config } from '../../config/schema.js';\nimport type { ChannelManager } from '../../channels/manager.js';\nimport { runWithLogContext, updateAsyncLogContext } from '../../utils/logger.js';\nimport type { ContextualLogger } from '../../utils/logger/types.js';\n\nimport type { AgentManager } from '../agent-manager.js';\nimport type { AgentOrchestrator } from '../orchestration/index.js';\nimport type { CommandHandler, OutboundCoordinator, StreamManager } from '../messaging/index.js';\nimport type { MessageRouter } from '../messaging/message-router.js';\nimport type { ModelManager } from '../models/index.js';\nimport type {\n SessionContext,\n SessionContextManager,\n SessionHydrator,\n SessionLifecycleManager,\n SessionStateBag,\n} from '../session/index.js';\nimport type { FeedbackCoordinator } from '../feedback/index.js';\nimport type { HookHandler } from '../lifecycle/hook-handler.js';\nimport type { SessionStore } from '../../session/store.js';\nimport { initSessionTurn } from '../../session/init-session-turn.js';\nimport { shouldSkipResetOverlapCommand } from '../../session/reset-triggers.js';\nimport type { StreamHandle } from '../service.types.js';\nimport { runEmbeddedTurnForSession } from '../embedded/run-for-session.js';\nimport { inboundMessageLogRequestId } from '../service-inbound-utils.js';\n\n/** Initial back-off when bus consume fails. Doubles each consecutive failure. */\nconst INBOUND_CONSUME_BASE_BACKOFF_MS = 1_000;\n/** Hard cap on back-off so a long bus outage settles at one retry per 30s. */\nconst INBOUND_CONSUME_MAX_BACKOFF_MS = 30_000;\n\nexport interface InboundLoopConfig {\n log: ContextualLogger;\n agentId: string;\n bus: MessageBus;\n hookHandler: HookHandler;\n messageRouter: MessageRouter;\n commandHandler: CommandHandler;\n sessionContextManager: SessionContextManager;\n feedbackCoordinator: FeedbackCoordinator;\n agentManager: AgentManager;\n sessionLifecycleManager: SessionLifecycleManager;\n agentOrchestrator: AgentOrchestrator;\n outboundCoordinator: OutboundCoordinator;\n streamManager: StreamManager;\n sessionState: SessionStateBag;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n\n /** Register a per-session agent-event subscription (idempotent). */\n setupSessionEventHandling: (sessionKey: string) => void;\n /** Per-session config hydration (workspace + model override) before a turn runs. */\n sessionHydrator: SessionHydrator;\n /** Returns the visible last assistant text used for outbound + persistent-goal hook. */\n getLastAssistantPlainText: (sessionKey: string) => string;\n /** Pre-turn auto-compaction (only used by system messages). */\n checkAndCompact: (sessionKey: string, messages: AgentMessage[]) => Promise<void>;\n /** Fire-and-forget auto-title (no-ops for cron/heartbeat keys). */\n enqueueMaybeAutoTitleAfterPersist: (sessionKey: string) => void;\n /** Effective merged config snapshot. */\n getConfig: () => Config | undefined;\n /** Archive transcript + new session id (freshness / reset trigger rollover). */\n resetSession: (sessionKey: string) => Promise<{ sessionId: string; previousSessionId: string } | null>;\n /** Connect a channel stream handle for partial assistant text rendering. */\n setStreamHandle: (handle: StreamHandle) => void;\n}\n\nexport class InboundLoop {\n private readonly cfg: InboundLoopConfig;\n private readonly log: ContextualLogger;\n private running = false;\n private channelManagerRef: ChannelManager | null = null;\n\n constructor(cfg: InboundLoopConfig) {\n this.cfg = cfg;\n this.log = cfg.log;\n }\n\n setChannelManager(channelManager: ChannelManager | null): void {\n this.channelManagerRef = channelManager;\n }\n\n isRunning(): boolean {\n return this.running;\n }\n\n /** Begin consuming inbound messages from the bus until {@link stop} or shutdown. */\n async start(): Promise<void> {\n this.running = true;\n await this.cfg.hookHandler.trigger('session_start', { sessionId: this.cfg.agentId });\n\n // Errors fall into two buckets:\n // 1. `bus.consumeInbound()` itself threw → infrastructure problem; back off\n // exponentially so a flapping bus does not pin the CPU at 100%.\n // 2. `handleInboundMessage(msg)` threw → per-message handler bug. The\n // message is already lost; do NOT sleep, just log and move on so\n // legitimate traffic is not held hostage.\n // Shutdown (`MessageBusShutdownError`) is the only signal that breaks the loop.\n let consecutiveConsumeFailures = 0;\n\n while (this.running) {\n let msg: InboundMessage;\n try {\n msg = await this.cfg.bus.consumeInbound();\n consecutiveConsumeFailures = 0;\n } catch (error) {\n if (error instanceof MessageBusShutdownError) {\n break;\n }\n consecutiveConsumeFailures += 1;\n const delayMs = Math.min(\n INBOUND_CONSUME_MAX_BACKOFF_MS,\n INBOUND_CONSUME_BASE_BACKOFF_MS * 2 ** (consecutiveConsumeFailures - 1),\n );\n const em = error instanceof Error ? error.message : String(error);\n this.log.error(\n {\n err: error,\n errorMessage: em,\n phase: 'inbound_consume',\n consecutiveFailures: consecutiveConsumeFailures,\n backoffMs: delayMs,\n },\n `Bus consume failed (backing off ${delayMs}ms): ${em}`,\n );\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n continue;\n }\n\n try {\n await this.handleInboundMessage(msg);\n } catch (error) {\n const em = error instanceof Error ? error.message : String(error);\n this.log.error(\n {\n err: error,\n errorMessage: em,\n phase: 'inbound_handle',\n channel: msg.channel,\n chatId: msg.chat_id,\n },\n `Inbound message handler failed: ${em}`,\n );\n // Per-message failure: keep consuming. The next message is independent.\n }\n }\n\n await this.cfg.hookHandler.trigger('session_end', {\n sessionId: this.cfg.agentId,\n messageCount: 0,\n });\n }\n\n /** Cooperatively stop the loop; the current message (if any) is allowed to finish. */\n stop(): void {\n this.running = false;\n }\n\n private async handleInboundMessage(msg: InboundMessage): Promise<void> {\n const requestId = inboundMessageLogRequestId(msg);\n\n await runWithLogContext({ requestId }, async () => {\n const routing = await this.cfg.messageRouter.routeMessage(msg);\n const { context, isCommand, command, commandArgs } = routing;\n\n const sessionContext: SessionContext = {\n sessionKey: context.sessionKey,\n channel: context.channel,\n chatId: context.chatId,\n senderId: context.senderId || '',\n isGroup: context.isGroup || false,\n metadata: {\n transcribedVoice: msg.metadata?.transcribedVoice === true,\n },\n };\n\n updateAsyncLogContext({ sessionId: sessionContext.sessionKey });\n\n await this.cfg.sessionContextManager.runWith(sessionContext, async () => {\n this.cfg.feedbackCoordinator.setContext(sessionContext);\n\n // `subscribeToSession` requires an Agent instance; without this the first inbound never\n // registers `message_update` streaming (second turn behaved differently).\n this.cfg.agentManager.getOrCreateAgent(sessionContext.sessionKey);\n this.cfg.setupSessionEventHandling(sessionContext.sessionKey);\n\n await this.cfg.sessionLifecycleManager.startSession(sessionContext);\n\n let typingController: ReturnType<OutboundCoordinator['createTypingControllerForInbound']> = null;\n let inboundTurnArmed = false;\n let busProcessFailed: string | undefined;\n let inboundMsg = msg;\n\n try {\n if (msg.channel === 'system') {\n await this.handleSystemMessage(msg, sessionContext);\n return;\n }\n\n if (this.channelManagerRef && msg.channel !== 'cli') {\n await this.channelManagerRef.dispatchInboundMessageAction(msg);\n }\n\n const cfg = this.cfg.getConfig();\n let effectiveContent = msg.content;\n let resetTriggeredAtInit = false;\n\n if (cfg && typeof msg.content === 'string') {\n const turn = await initSessionTurn({\n cfg,\n sessionKey: sessionContext.sessionKey,\n body: msg.content,\n resetSession: (sk) => this.cfg.resetSession(sk),\n });\n resetTriggeredAtInit = turn.resetTriggered;\n\n if (turn.bareReset && turn.ackMessage) {\n await this.cfg.bus.publishOutbound({\n channel: sessionContext.channel,\n chat_id: sessionContext.chatId,\n content: turn.ackMessage,\n type: 'message',\n });\n return;\n }\n\n effectiveContent = turn.bodyStripped;\n }\n\n if (isCommand && command) {\n if (!shouldSkipResetOverlapCommand(command, resetTriggeredAtInit)) {\n const handled = await this.cfg.commandHandler.executeCommand(command, commandArgs || '', {\n sessionKey: sessionContext.sessionKey,\n channel: sessionContext.channel,\n chatId: sessionContext.chatId,\n senderId: sessionContext.senderId,\n isGroup: sessionContext.isGroup,\n inboundMetadata: msg.metadata,\n });\n if (handled) {\n return;\n }\n }\n }\n\n inboundMsg =\n effectiveContent !== msg.content ? { ...msg, content: effectiveContent } : msg;\n\n // Continuous typing indicator (renews every 5 seconds); stopped only AFTER outbound.\n typingController = this.cfg.outboundCoordinator.createTypingControllerForInbound(msg);\n typingController?.start();\n\n if (this.channelManagerRef && msg.channel !== 'cli') {\n const meta = msg.metadata as Record<string, unknown> | undefined;\n const streamHandle = this.channelManagerRef.startStream(\n msg.channel,\n msg.chat_id,\n meta?.accountId as string | undefined,\n {\n threadId: meta?.threadId as string | undefined,\n replyToMessageId: meta?.messageId as string | undefined,\n },\n );\n if (streamHandle) {\n this.cfg.setStreamHandle(streamHandle as StreamHandle);\n }\n }\n\n this.cfg.sessionState.beginInboundTurn(sessionContext.sessionKey);\n inboundTurnArmed = true;\n try {\n await this.cfg.agentOrchestrator.process(inboundMsg, sessionContext);\n } catch (procErr) {\n busProcessFailed = procErr instanceof Error ? procErr.message : String(procErr);\n throw procErr;\n }\n } finally {\n await this.cfg.sessionLifecycleManager.endSession(sessionContext);\n await this.cfg.streamManager.end();\n try {\n await this.cfg.outboundCoordinator.sendFinalResponse(msg, sessionContext);\n } finally {\n // Clear typing AFTER outbound (incl. TTS); otherwise Weixin shows typing_off before the message.\n await typingController?.stop();\n }\n if (inboundTurnArmed) {\n const meta = msg.metadata as Record<string, unknown> | undefined;\n const assistantPlainText = this.cfg.getLastAssistantPlainText(sessionContext.sessionKey) ?? '';\n try {\n await this.cfg.outboundCoordinator.emitSessionTurnComplete({\n sessionKey: sessionContext.sessionKey,\n channel: sessionContext.channel,\n chatId: sessionContext.chatId,\n inboundUserText: inboundMsg.content,\n assistantPlainText,\n aborted: false,\n ...(busProcessFailed !== undefined ? { streamError: busProcessFailed } : {}),\n skipPersistentGoalPostTurn: false,\n outboundMetadata: {\n accountId: meta?.accountId,\n threadId: meta?.threadId,\n },\n });\n } catch (turnErr) {\n const em = turnErr instanceof Error ? turnErr.message : String(turnErr);\n this.log.warn(\n { err: turnErr, sessionKey: sessionContext.sessionKey },\n `Session turn complete failed: ${em}`,\n );\n }\n this.cfg.sessionState.endInboundTurn(sessionContext.sessionKey);\n }\n this.cfg.feedbackCoordinator.endTask();\n this.cfg.feedbackCoordinator.clearContext();\n }\n });\n });\n }\n\n private async handleSystemMessage(msg: InboundMessage, context: SessionContext): Promise<void> {\n this.log.debug({ sessionKey: context.sessionKey }, 'Processing system message');\n\n await this.cfg.sessionHydrator.workspace(context.sessionKey);\n await this.cfg.sessionHydrator.model(context.sessionKey);\n\n const messages = await this.cfg.sessionStore.load(context.sessionKey);\n await this.cfg.checkAndCompact(context.sessionKey, messages);\n\n const systemMessage: AgentMessage = {\n role: 'user',\n content: [{ type: 'text', text: `[System: ${msg.sender_id}] ${msg.content}` }],\n timestamp: Date.now(),\n };\n\n try {\n const result = await runEmbeddedTurnForSession({\n sessionKey: context.sessionKey,\n userMessage: systemMessage,\n sessionStore: this.cfg.sessionStore,\n agentManager: this.cfg.agentManager,\n modelManager: this.cfg.modelManager,\n getConfig: this.cfg.getConfig,\n });\n\n const finalContent = result.lastAssistantText ?? this.cfg.getLastAssistantPlainText(context.sessionKey);\n if (finalContent) {\n this.cfg.sessionState.setLastAssistantText(context.sessionKey, finalContent);\n const hookResult = await this.cfg.hookHandler.runMessageSending(\n context.chatId,\n finalContent,\n context.channel,\n );\n if (hookResult.send) {\n await this.cfg.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: hookResult.content || finalContent,\n type: 'message',\n });\n }\n }\n this.cfg.enqueueMaybeAutoTitleAfterPersist(context.sessionKey);\n } catch (error) {\n const em = error instanceof Error ? error.message : String(error);\n this.log.error(\n {\n err: error,\n errorMessage: em,\n sessionKey: context.sessionKey,\n channel: context.channel,\n chatId: context.chatId,\n senderId: msg.sender_id,\n },\n `System message handling failed: ${em}`,\n );\n await this.cfg.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: '❌ An error occurred while processing the system message.',\n type: 'message',\n });\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"inbound-loop.js","names":[],"sources":["../../../../src/agent/inbound/inbound-loop.ts"],"sourcesContent":["/**\n * InboundLoop — owns the bus-driven inbound pipeline.\n *\n * Replaces the previous `AgentService.start()` while-loop, `handleInboundMessage`,\n * and `handleSystemMessage` methods. Everything that \"a message arrives, run the\n * agent turn, publish the reply\" needs is concentrated here; the parent\n * `AgentService` now just constructs the loop with collaborators and forwards\n * start/stop to it.\n *\n * The constructor takes a fairly wide config because handling a single inbound\n * message touches almost every subsystem (router, command handler, lifecycle\n * manager, agent orchestrator, outbound coordinator, session state, etc.). All\n * dependencies are injected so this class is unit-testable and the inbound\n * pipeline can evolve without touching `AgentService`.\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\n\nimport type { MessageBus, InboundMessage } from '../../infra/bus/index.js';\nimport { MessageBusShutdownError } from '../../infra/bus/index.js';\nimport type { Config } from '../../config/schema.js';\nimport type { ChannelManager } from '../../channels/manager.js';\nimport { runWithLogContext, updateAsyncLogContext } from '../../utils/logger.js';\nimport type { ContextualLogger } from '../../utils/logger/types.js';\n\nimport type { AgentManager } from '../agent-manager.js';\nimport type { AgentOrchestrator } from '../orchestration/index.js';\nimport type { CommandHandler, OutboundCoordinator, StreamManager } from '../messaging/index.js';\nimport type { MessageRouter } from '../messaging/message-router.js';\nimport type { ModelManager } from '../models/index.js';\nimport type {\n SessionContext,\n SessionContextManager,\n SessionHydrator,\n SessionLifecycleManager,\n SessionStateBag,\n} from '../session/index.js';\nimport type { FeedbackCoordinator } from '../feedback/index.js';\nimport type { HookHandler } from '../lifecycle/hook-handler.js';\nimport type { SessionStore } from '../../session/store.js';\nimport { initSessionTurn } from '../../session/init-session-turn.js';\nimport { shouldSkipResetOverlapCommand } from '../../session/reset-triggers.js';\nimport type { StreamHandle } from '../service.types.js';\nimport { runEmbeddedTurnForSession } from '../embedded/run-for-session.js';\nimport { inboundMessageLogRequestId } from '../service-inbound-utils.js';\n\n/** Initial back-off when bus consume fails. Doubles each consecutive failure. */\nconst INBOUND_CONSUME_BASE_BACKOFF_MS = 1_000;\n/** Hard cap on back-off so a long bus outage settles at one retry per 30s. */\nconst INBOUND_CONSUME_MAX_BACKOFF_MS = 30_000;\n\nexport interface InboundLoopConfig {\n log: ContextualLogger;\n agentId: string;\n bus: MessageBus;\n hookHandler: HookHandler;\n messageRouter: MessageRouter;\n commandHandler: CommandHandler;\n sessionContextManager: SessionContextManager;\n feedbackCoordinator: FeedbackCoordinator;\n agentManager: AgentManager;\n sessionLifecycleManager: SessionLifecycleManager;\n agentOrchestrator: AgentOrchestrator;\n outboundCoordinator: OutboundCoordinator;\n streamManager: StreamManager;\n sessionState: SessionStateBag;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n\n /** Register a per-session agent-event subscription (idempotent). */\n setupSessionEventHandling: (sessionKey: string) => void;\n /** Per-session config hydration (workspace + model override) before a turn runs. */\n sessionHydrator: SessionHydrator;\n /** Returns the visible last assistant text used for outbound + persistent-goal hook. */\n getLastAssistantPlainText: (sessionKey: string) => string;\n /** Pre-turn auto-compaction (only used by system messages). */\n checkAndCompact: (sessionKey: string, messages: AgentMessage[]) => Promise<void>;\n /** Fire-and-forget auto-title (no-ops for cron/heartbeat keys). */\n enqueueMaybeAutoTitleAfterPersist: (sessionKey: string) => void;\n /** Effective merged config snapshot. */\n getConfig: () => Config | undefined;\n /** Archive transcript + new session id (freshness / reset trigger rollover). */\n resetSession: (sessionKey: string) => Promise<{ sessionId: string; previousSessionId: string } | null>;\n /** Connect a channel stream handle for partial assistant text rendering. */\n setStreamHandle: (handle: StreamHandle) => void;\n}\n\nexport class InboundLoop {\n private readonly cfg: InboundLoopConfig;\n private readonly log: ContextualLogger;\n private running = false;\n private channelManagerRef: ChannelManager | null = null;\n\n constructor(cfg: InboundLoopConfig) {\n this.cfg = cfg;\n this.log = cfg.log;\n }\n\n setChannelManager(channelManager: ChannelManager | null): void {\n this.channelManagerRef = channelManager;\n }\n\n isRunning(): boolean {\n return this.running;\n }\n\n /** Begin consuming inbound messages from the bus until {@link stop} or shutdown. */\n async start(): Promise<void> {\n this.running = true;\n await this.cfg.hookHandler.trigger('session_start', { sessionId: this.cfg.agentId });\n\n // Errors fall into two buckets:\n // 1. `bus.consumeInbound()` itself threw → infrastructure problem; back off\n // exponentially so a flapping bus does not pin the CPU at 100%.\n // 2. `handleInboundMessage(msg)` threw → per-message handler bug. The\n // message is already lost; do NOT sleep, just log and move on so\n // legitimate traffic is not held hostage.\n // Shutdown (`MessageBusShutdownError`) is the only signal that breaks the loop.\n let consecutiveConsumeFailures = 0;\n\n while (this.running) {\n let msg: InboundMessage;\n try {\n msg = await this.cfg.bus.consumeInbound();\n consecutiveConsumeFailures = 0;\n } catch (error) {\n if (error instanceof MessageBusShutdownError) {\n break;\n }\n consecutiveConsumeFailures += 1;\n const delayMs = Math.min(\n INBOUND_CONSUME_MAX_BACKOFF_MS,\n INBOUND_CONSUME_BASE_BACKOFF_MS * 2 ** (consecutiveConsumeFailures - 1),\n );\n const em = error instanceof Error ? error.message : String(error);\n this.log.error(\n {\n err: error,\n errorMessage: em,\n phase: 'inbound_consume',\n consecutiveFailures: consecutiveConsumeFailures,\n backoffMs: delayMs,\n },\n `Bus consume failed (backing off ${delayMs}ms): ${em}`,\n );\n await new Promise((resolve) => setTimeout(resolve, delayMs));\n continue;\n }\n\n try {\n await this.handleInboundMessage(msg);\n } catch (error) {\n const em = error instanceof Error ? error.message : String(error);\n this.log.error(\n {\n err: error,\n errorMessage: em,\n phase: 'inbound_handle',\n channel: msg.channel,\n chatId: msg.chat_id,\n },\n `Inbound message handler failed: ${em}`,\n );\n // Per-message failure: keep consuming. The next message is independent.\n }\n }\n\n await this.cfg.hookHandler.trigger('session_end', {\n sessionId: this.cfg.agentId,\n messageCount: 0,\n });\n }\n\n /** Cooperatively stop the loop; the current message (if any) is allowed to finish. */\n stop(): void {\n this.running = false;\n }\n\n private async handleInboundMessage(msg: InboundMessage): Promise<void> {\n const requestId = inboundMessageLogRequestId(msg);\n\n await runWithLogContext({ requestId }, async () => {\n const routing = await this.cfg.messageRouter.routeMessage(msg);\n const { context, isCommand, command, commandArgs } = routing;\n\n const sessionContext: SessionContext = {\n sessionKey: context.sessionKey,\n channel: context.channel,\n chatId: context.chatId,\n senderId: context.senderId || '',\n isGroup: context.isGroup || false,\n metadata: {\n transcribedVoice: msg.metadata?.transcribedVoice === true,\n },\n };\n\n const sessionMetadata = await this.cfg.sessionStore.getMetadata(sessionContext.sessionKey).catch(() => null);\n updateAsyncLogContext({\n sessionKey: sessionContext.sessionKey,\n ...(sessionMetadata?.sessionId ? { sessionId: sessionMetadata.sessionId } : {}),\n });\n\n await this.cfg.sessionContextManager.runWith(sessionContext, async () => {\n this.cfg.feedbackCoordinator.setContext(sessionContext);\n\n // `subscribeToSession` requires an Agent instance; without this the first inbound never\n // registers `message_update` streaming (second turn behaved differently).\n this.cfg.agentManager.getOrCreateAgent(sessionContext.sessionKey);\n this.cfg.setupSessionEventHandling(sessionContext.sessionKey);\n\n await this.cfg.sessionLifecycleManager.startSession(sessionContext);\n\n let typingController: ReturnType<OutboundCoordinator['createTypingControllerForInbound']> = null;\n let inboundTurnArmed = false;\n let busProcessFailed: string | undefined;\n let inboundMsg = msg;\n\n try {\n if (msg.channel === 'system') {\n await this.handleSystemMessage(msg, sessionContext);\n return;\n }\n\n if (this.channelManagerRef && msg.channel !== 'cli') {\n await this.channelManagerRef.dispatchInboundMessageAction(msg);\n }\n\n const cfg = this.cfg.getConfig();\n let effectiveContent = msg.content;\n let resetTriggeredAtInit = false;\n\n if (cfg && typeof msg.content === 'string') {\n const turn = await initSessionTurn({\n cfg,\n sessionKey: sessionContext.sessionKey,\n body: msg.content,\n resetSession: (sk) => this.cfg.resetSession(sk),\n });\n resetTriggeredAtInit = turn.resetTriggered;\n\n if (turn.bareReset && turn.ackMessage) {\n await this.cfg.bus.publishOutbound({\n channel: sessionContext.channel,\n chat_id: sessionContext.chatId,\n content: turn.ackMessage,\n type: 'message',\n });\n return;\n }\n\n effectiveContent = turn.bodyStripped;\n }\n\n if (isCommand && command) {\n if (!shouldSkipResetOverlapCommand(command, resetTriggeredAtInit)) {\n const handled = await this.cfg.commandHandler.executeCommand(command, commandArgs || '', {\n sessionKey: sessionContext.sessionKey,\n channel: sessionContext.channel,\n chatId: sessionContext.chatId,\n senderId: sessionContext.senderId,\n isGroup: sessionContext.isGroup,\n inboundMetadata: msg.metadata,\n });\n if (handled) {\n return;\n }\n }\n }\n\n inboundMsg =\n effectiveContent !== msg.content ? { ...msg, content: effectiveContent } : msg;\n\n // Continuous typing indicator (renews every 5 seconds); stopped only AFTER outbound.\n typingController = this.cfg.outboundCoordinator.createTypingControllerForInbound(msg);\n typingController?.start();\n\n if (this.channelManagerRef && msg.channel !== 'cli') {\n const meta = msg.metadata as Record<string, unknown> | undefined;\n const streamHandle = this.channelManagerRef.startStream(\n msg.channel,\n msg.chat_id,\n meta?.accountId as string | undefined,\n {\n threadId: meta?.threadId as string | undefined,\n replyToMessageId: meta?.messageId as string | undefined,\n },\n );\n if (streamHandle) {\n this.cfg.setStreamHandle(streamHandle as StreamHandle);\n }\n }\n\n this.cfg.sessionState.beginInboundTurn(sessionContext.sessionKey);\n inboundTurnArmed = true;\n try {\n await this.cfg.agentOrchestrator.process(inboundMsg, sessionContext);\n } catch (procErr) {\n busProcessFailed = procErr instanceof Error ? procErr.message : String(procErr);\n throw procErr;\n }\n } finally {\n await this.cfg.sessionLifecycleManager.endSession(sessionContext);\n await this.cfg.streamManager.end();\n try {\n await this.cfg.outboundCoordinator.sendFinalResponse(msg, sessionContext);\n } finally {\n // Clear typing AFTER outbound (incl. TTS); otherwise Weixin shows typing_off before the message.\n await typingController?.stop();\n }\n if (inboundTurnArmed) {\n const meta = msg.metadata as Record<string, unknown> | undefined;\n const assistantPlainText = this.cfg.getLastAssistantPlainText(sessionContext.sessionKey) ?? '';\n try {\n await this.cfg.outboundCoordinator.emitSessionTurnComplete({\n sessionKey: sessionContext.sessionKey,\n channel: sessionContext.channel,\n chatId: sessionContext.chatId,\n inboundUserText: inboundMsg.content,\n assistantPlainText,\n aborted: false,\n ...(busProcessFailed !== undefined ? { streamError: busProcessFailed } : {}),\n skipPersistentGoalPostTurn: false,\n outboundMetadata: {\n accountId: meta?.accountId,\n threadId: meta?.threadId,\n },\n });\n } catch (turnErr) {\n const em = turnErr instanceof Error ? turnErr.message : String(turnErr);\n this.log.warn(\n { err: turnErr, sessionKey: sessionContext.sessionKey },\n `Session turn complete failed: ${em}`,\n );\n }\n this.cfg.sessionState.endInboundTurn(sessionContext.sessionKey);\n }\n this.cfg.feedbackCoordinator.endTask();\n this.cfg.feedbackCoordinator.clearContext();\n }\n });\n });\n }\n\n private async handleSystemMessage(msg: InboundMessage, context: SessionContext): Promise<void> {\n this.log.debug({ sessionKey: context.sessionKey }, 'Processing system message');\n\n await this.cfg.sessionHydrator.workspace(context.sessionKey);\n await this.cfg.sessionHydrator.model(context.sessionKey);\n\n const messages = await this.cfg.sessionStore.load(context.sessionKey);\n await this.cfg.checkAndCompact(context.sessionKey, messages);\n\n const systemMessage: AgentMessage = {\n role: 'user',\n content: [{ type: 'text', text: `[System: ${msg.sender_id}] ${msg.content}` }],\n timestamp: Date.now(),\n };\n\n try {\n const result = await runEmbeddedTurnForSession({\n sessionKey: context.sessionKey,\n userMessage: systemMessage,\n sessionStore: this.cfg.sessionStore,\n agentManager: this.cfg.agentManager,\n modelManager: this.cfg.modelManager,\n getConfig: this.cfg.getConfig,\n });\n\n const finalContent = result.lastAssistantText ?? this.cfg.getLastAssistantPlainText(context.sessionKey);\n if (finalContent) {\n this.cfg.sessionState.setLastAssistantText(context.sessionKey, finalContent);\n const hookResult = await this.cfg.hookHandler.runMessageSending(\n context.chatId,\n finalContent,\n context.channel,\n );\n if (hookResult.send) {\n await this.cfg.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: hookResult.content || finalContent,\n type: 'message',\n });\n }\n }\n this.cfg.enqueueMaybeAutoTitleAfterPersist(context.sessionKey);\n } catch (error) {\n const em = error instanceof Error ? error.message : String(error);\n this.log.error(\n {\n err: error,\n errorMessage: em,\n sessionKey: context.sessionKey,\n channel: context.channel,\n chatId: context.chatId,\n senderId: msg.sender_id,\n },\n `System message handling failed: ${em}`,\n );\n await this.cfg.bus.publishOutbound({\n channel: context.channel,\n chat_id: context.chatId,\n content: '❌ An error occurred while processing the system message.',\n type: 'message',\n });\n }\n }\n}\n"],"mappings":"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"}
@@ -36,10 +36,10 @@ export interface TurnDispatcherConfig {
36
36
  getConfig: () => Config | undefined;
37
37
  /** Strict accessor — required for direct-turn paths that must have a config. */
38
38
  requireConfig: () => Config;
39
- parseSessionKey: (sessionKey: string) => {
39
+ resolveSessionEndpoint: (sessionKey: string) => Promise<{
40
40
  channel: string;
41
41
  chatId: string;
42
- };
42
+ }>;
43
43
  /** Establish per-session context (also creates the Agent + subscribes to events). */
44
44
  initSessionContext: (sessionKey: string, channel: string, chatId: string) => SessionContext;
45
45
  /** Per-session config hydration: workspace, model, thinking. */
@@ -85,7 +85,7 @@ var TurnDispatcher = class {
85
85
  const c = this.cfg;
86
86
  return {
87
87
  log: this.log,
88
- parseSessionKey: c.parseSessionKey,
88
+ resolveSessionEndpoint: c.resolveSessionEndpoint,
89
89
  initDirectStreamingSession: c.initSessionContext,
90
90
  registerWebchatSsePublisher: (sk, publisher) => c.sessionState.registerWebchatPublisher(sk, publisher),
91
91
  unregisterWebchatSsePublisher: (sk) => c.sessionState.unregisterWebchatPublisher(sk),
@@ -135,7 +135,7 @@ var TurnDispatcher = class {
135
135
  return {
136
136
  log: this.log,
137
137
  config: cfg,
138
- parseSessionKey: c.parseSessionKey,
138
+ resolveSessionEndpoint: c.resolveSessionEndpoint,
139
139
  initSessionContext: (sk, channel, chatId) => {
140
140
  c.initSessionContext(sk, channel, chatId);
141
141
  },
@@ -1 +1 @@
1
- {"version":3,"file":"turn-dispatcher.js","names":[],"sources":["../../../../src/agent/inbound/turn-dispatcher.ts"],"sourcesContent":["/**\n * TurnDispatcher — single entry point for direct (non-bus) agent turns.\n *\n * Wraps the two existing direct-turn runners (one-shot and streaming) so the\n * parent `AgentService` no longer carries a pair of huge `createXxxDeps()`\n * factories. The public surface — `processDirect`, `processDirectStreaming`,\n * `steerWebchatSession`, `enqueueWebchatSseEvent`,\n * `notifyWebchatTranscriptAppend` — matches what `AgentService` exposed\n * previously, so callers (gateway, CLI) are unchanged.\n *\n * Like `OutboundCoordinator` and `InboundLoop`, this class accepts a wide\n * dependency bag in its constructor (the direct-turn pipeline touches almost\n * every other subsystem). The win is that AgentService becomes the only place\n * that wires those dependencies together; individual responsibilities now live\n * in their own classes and are unit-testable.\n */\n\nimport type { Config } from '../../config/schema.js';\nimport type { ContextualLogger } from '../../utils/logger/types.js';\n\nimport type { AgentManager } from '../agent-manager.js';\nimport type { CommandHandler } from '../messaging/command-handler.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { SessionConfigStore } from '../../session/index.js';\nimport type { SessionStore } from '../../session/store.js';\nimport type {\n SessionContext,\n SessionHydrator,\n SessionStateBag,\n} from '../session/index.js';\nimport { queueEmbeddedSteer } from '../embedded/runs.js';\nimport type {\n InboundAttachmentInput,\n MediaRef,\n} from '../../channels/attachments/inbound-persist.js';\nimport {\n buildDirectUserMessageContent,\n} from '../service/build-direct-message-content.js';\nimport {\n runProcessDirectStreaming,\n type ProcessDirectStreamingDeps,\n type ProcessDirectStreamingSseEvent,\n} from '../service/process-direct-streaming.js';\nimport {\n runProcessDirect,\n type RunProcessDirectDeps,\n} from '../service/process-direct-one-shot.js';\nimport { maybeEmitWebchatTts } from '../service/webchat-tts.js';\nimport type { AgentSourceContextResolver } from '../source-context/types.js';\n\nexport interface TurnDispatcherConfig {\n log: ContextualLogger;\n agentManager: AgentManager;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n sessionConfigStore: SessionConfigStore;\n sessionState: SessionStateBag;\n commandHandler: CommandHandler;\n getConfig: () => Config | undefined;\n /** Strict accessor — required for direct-turn paths that must have a config. */\n requireConfig: () => Config;\n parseSessionKey: (sessionKey: string) => { channel: string; chatId: string };\n /** Establish per-session context (also creates the Agent + subscribes to events). */\n initSessionContext: (sessionKey: string, channel: string, chatId: string) => SessionContext;\n /** Per-session config hydration: workspace, model, thinking. */\n sessionHydrator: SessionHydrator;\n prepareInboundAttachments: (\n sessionKey: string,\n attachments?: InboundAttachmentInput[],\n ) => Promise<MediaRef[] | undefined>;\n enqueueMaybeAutoTitleAfterPersist: (sessionKey: string) => void;\n enqueueProvisionalSessionTitle?: (sessionKey: string, userText: string) => void;\n endDirectRequestContext: () => void;\n /** Gateway hook fired after assistant text lands on disk (UI refetch). */\n onSessionTranscriptUpdated?: (sessionKey: string) => void;\n resetSession: (sessionKey: string) => Promise<{ sessionId: string; previousSessionId: string } | null>;\n sourceContextResolver?: AgentSourceContextResolver;\n}\n\nexport type DirectAttachment = InboundAttachmentInput;\n\nexport class TurnDispatcher {\n private readonly cfg: TurnDispatcherConfig;\n private readonly log: ContextualLogger;\n\n constructor(cfg: TurnDispatcherConfig) {\n this.cfg = cfg;\n this.log = cfg.log;\n }\n\n /** One-shot direct turn (CLI / embedded TUI). */\n processDirect(\n content: string,\n sessionKey = 'agent:main:main',\n attachments?: DirectAttachment[],\n thinking?: string,\n ): Promise<string> {\n return runProcessDirect(this.buildOneShotDeps(), {\n content,\n sessionKey,\n attachments,\n thinking,\n });\n }\n\n /** Streaming direct turn (webchat SSE / CLI streaming). */\n async *processDirectStreaming(\n content: string,\n sessionKey = 'agent:main:main',\n attachments?: DirectAttachment[],\n thinking?: string,\n options?: { signal?: AbortSignal; runId?: string },\n ): AsyncGenerator<ProcessDirectStreamingSseEvent, void, unknown> {\n yield* runProcessDirectStreaming(this.buildStreamingDeps(), {\n content,\n sessionKey,\n attachments,\n thinking,\n signal: options?.signal,\n runId: options?.runId,\n });\n }\n\n /** Push an out-of-band event into the live webchat stream for a session. */\n enqueueWebchatSseEvent(\n sessionKey: string,\n event: { type: string; [key: string]: unknown },\n ): void {\n const pub = this.cfg.sessionState.getWebchatPublisher(sessionKey);\n if (pub) {\n pub(event);\n }\n }\n\n /** Stream assistant text to live webchat session + notify transcript listeners. */\n notifyWebchatTranscriptAppend(sessionKey: string, assistantText: string): void {\n const trimmed = assistantText.trim();\n if (trimmed) {\n const message = {\n role: 'assistant',\n content: [{ type: 'text', text: trimmed }],\n timestamp: Date.now(),\n };\n this.enqueueWebchatSseEvent(sessionKey, { type: 'message_start', message });\n this.enqueueWebchatSseEvent(sessionKey, { type: 'message_update', message });\n this.enqueueWebchatSseEvent(sessionKey, { type: 'message_end', message });\n }\n this.cfg.onSessionTranscriptUpdated?.(sessionKey);\n }\n\n /**\n * Queue a steering user message into pi-agent's in-flight run (delivered\n * after current tool work, before the next LLM call). See `Agent.steer`\n * in `@earendil-works/pi-agent-core`.\n */\n async steerWebchatSession(sessionKey: string, text: string): Promise<boolean> {\n const trimmed = text.trim();\n if (!trimmed) return false;\n try {\n return await queueEmbeddedSteer(sessionKey, trimmed);\n } catch (err) {\n this.log.warn({ err, sessionKey }, 'steerWebchatSession failed');\n return false;\n }\n }\n\n private buildStreamingDeps(): ProcessDirectStreamingDeps {\n const c = this.cfg;\n return {\n log: this.log,\n parseSessionKey: c.parseSessionKey,\n initDirectStreamingSession: c.initSessionContext,\n registerWebchatSsePublisher: (sk, publisher) =>\n c.sessionState.registerWebchatPublisher(sk, publisher),\n unregisterWebchatSsePublisher: (sk) => c.sessionState.unregisterWebchatPublisher(sk),\n agentManager: c.agentManager,\n hydrateSessionWorkspaceFromStore: (sk) => c.sessionHydrator.workspace(sk),\n hydrateSessionModelFromStore: (sk) => c.sessionHydrator.model(sk),\n sessionStore: c.sessionStore,\n modelManager: c.modelManager,\n applyResolvedThinkingLevel: (sk, t) => c.sessionHydrator.thinking(sk, t),\n getConfig: c.getConfig,\n sessionConfigStore: c.sessionConfigStore,\n commandHandler: c.commandHandler,\n prepareInboundAttachments: c.prepareInboundAttachments,\n buildTranscriptUserMessage: async (text, prepared, sk) =>\n buildDirectUserMessageContent({\n content: text,\n attachments: prepared,\n sessionKey: sk,\n config: c.requireConfig(),\n agentManager: c.agentManager,\n modelManager: c.modelManager,\n }),\n recordPersistentGoalStreamOutcome: (sk, o) =>\n c.sessionState.recordPersistentGoalStreamOutcome(sk, o),\n enqueueProvisionalSessionTitle: (sk, userText) => {\n c.enqueueProvisionalSessionTitle?.(sk, userText);\n },\n onTurnComplete: (sk, text) => {\n if (text) {\n c.sessionState.setLastAssistantText(sk, text);\n }\n c.enqueueMaybeAutoTitleAfterPersist(sk);\n },\n reloadWebchatTranscript: (sk) => {\n c.onSessionTranscriptUpdated?.(sk);\n },\n sourceContextResolver: c.sourceContextResolver,\n maybeEmitWebchatTts: (sk, hadVoice) =>\n maybeEmitWebchatTts(\n {\n config: c.getConfig(),\n sessionStore: c.sessionStore,\n getLastAssistantPlainText: (sessionKey) =>\n c.sessionState.getLastAssistantText(sessionKey) ??\n c.agentManager.getLastAssistantContent(sessionKey) ??\n '',\n log: this.log,\n },\n sk,\n hadVoice,\n ),\n endDirectRequestContext: c.endDirectRequestContext,\n resetSession: c.resetSession,\n };\n }\n\n private buildOneShotDeps(): RunProcessDirectDeps {\n const c = this.cfg;\n const cfg = c.requireConfig();\n return {\n log: this.log,\n config: cfg,\n parseSessionKey: c.parseSessionKey,\n initSessionContext: (sk, channel, chatId) => {\n void c.initSessionContext(sk, channel, chatId);\n },\n hydrateSessionWorkspaceFromStore: (sk) => c.sessionHydrator.workspace(sk),\n hydrateSessionModelFromStore: (sk) => c.sessionHydrator.model(sk),\n agentManager: c.agentManager,\n sessionStore: c.sessionStore,\n modelManager: c.modelManager,\n applyResolvedThinkingLevel: (sk, t) => c.sessionHydrator.thinking(sk, t),\n prepareInboundAttachments: c.prepareInboundAttachments,\n commandHandler: c.commandHandler,\n onTurnComplete: (sk, text) => {\n if (text) {\n c.sessionState.setLastAssistantText(sk, text);\n }\n c.enqueueMaybeAutoTitleAfterPersist(sk);\n },\n endDirectRequestContext: c.endDirectRequestContext,\n resetSession: c.resetSession,\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"turn-dispatcher.js","names":[],"sources":["../../../../src/agent/inbound/turn-dispatcher.ts"],"sourcesContent":["/**\n * TurnDispatcher — single entry point for direct (non-bus) agent turns.\n *\n * Wraps the two existing direct-turn runners (one-shot and streaming) so the\n * parent `AgentService` no longer carries a pair of huge `createXxxDeps()`\n * factories. The public surface — `processDirect`, `processDirectStreaming`,\n * `steerWebchatSession`, `enqueueWebchatSseEvent`,\n * `notifyWebchatTranscriptAppend` — matches what `AgentService` exposed\n * previously, so callers (gateway, CLI) are unchanged.\n *\n * Like `OutboundCoordinator` and `InboundLoop`, this class accepts a wide\n * dependency bag in its constructor (the direct-turn pipeline touches almost\n * every other subsystem). The win is that AgentService becomes the only place\n * that wires those dependencies together; individual responsibilities now live\n * in their own classes and are unit-testable.\n */\n\nimport type { Config } from '../../config/schema.js';\nimport type { ContextualLogger } from '../../utils/logger/types.js';\n\nimport type { AgentManager } from '../agent-manager.js';\nimport type { CommandHandler } from '../messaging/command-handler.js';\nimport type { ModelManager } from '../models/index.js';\nimport type { SessionConfigStore } from '../../session/index.js';\nimport type { SessionStore } from '../../session/store.js';\nimport type {\n SessionContext,\n SessionHydrator,\n SessionStateBag,\n} from '../session/index.js';\nimport { queueEmbeddedSteer } from '../embedded/runs.js';\nimport type {\n InboundAttachmentInput,\n MediaRef,\n} from '../../channels/attachments/inbound-persist.js';\nimport {\n buildDirectUserMessageContent,\n} from '../service/build-direct-message-content.js';\nimport {\n runProcessDirectStreaming,\n type ProcessDirectStreamingDeps,\n type ProcessDirectStreamingSseEvent,\n} from '../service/process-direct-streaming.js';\nimport {\n runProcessDirect,\n type RunProcessDirectDeps,\n} from '../service/process-direct-one-shot.js';\nimport { maybeEmitWebchatTts } from '../service/webchat-tts.js';\nimport type { AgentSourceContextResolver } from '../source-context/types.js';\n\nexport interface TurnDispatcherConfig {\n log: ContextualLogger;\n agentManager: AgentManager;\n sessionStore: SessionStore;\n modelManager: ModelManager;\n sessionConfigStore: SessionConfigStore;\n sessionState: SessionStateBag;\n commandHandler: CommandHandler;\n getConfig: () => Config | undefined;\n /** Strict accessor — required for direct-turn paths that must have a config. */\n requireConfig: () => Config;\n resolveSessionEndpoint: (sessionKey: string) => Promise<{ channel: string; chatId: string }>;\n /** Establish per-session context (also creates the Agent + subscribes to events). */\n initSessionContext: (sessionKey: string, channel: string, chatId: string) => SessionContext;\n /** Per-session config hydration: workspace, model, thinking. */\n sessionHydrator: SessionHydrator;\n prepareInboundAttachments: (\n sessionKey: string,\n attachments?: InboundAttachmentInput[],\n ) => Promise<MediaRef[] | undefined>;\n enqueueMaybeAutoTitleAfterPersist: (sessionKey: string) => void;\n enqueueProvisionalSessionTitle?: (sessionKey: string, userText: string) => void;\n endDirectRequestContext: () => void;\n /** Gateway hook fired after assistant text lands on disk (UI refetch). */\n onSessionTranscriptUpdated?: (sessionKey: string) => void;\n resetSession: (sessionKey: string) => Promise<{ sessionId: string; previousSessionId: string } | null>;\n sourceContextResolver?: AgentSourceContextResolver;\n}\n\nexport type DirectAttachment = InboundAttachmentInput;\n\nexport class TurnDispatcher {\n private readonly cfg: TurnDispatcherConfig;\n private readonly log: ContextualLogger;\n\n constructor(cfg: TurnDispatcherConfig) {\n this.cfg = cfg;\n this.log = cfg.log;\n }\n\n /** One-shot direct turn (CLI / embedded TUI). */\n processDirect(\n content: string,\n sessionKey = 'agent:main:main',\n attachments?: DirectAttachment[],\n thinking?: string,\n ): Promise<string> {\n return runProcessDirect(this.buildOneShotDeps(), {\n content,\n sessionKey,\n attachments,\n thinking,\n });\n }\n\n /** Streaming direct turn (webchat SSE / CLI streaming). */\n async *processDirectStreaming(\n content: string,\n sessionKey = 'agent:main:main',\n attachments?: DirectAttachment[],\n thinking?: string,\n options?: { signal?: AbortSignal; runId?: string },\n ): AsyncGenerator<ProcessDirectStreamingSseEvent, void, unknown> {\n yield* runProcessDirectStreaming(this.buildStreamingDeps(), {\n content,\n sessionKey,\n attachments,\n thinking,\n signal: options?.signal,\n runId: options?.runId,\n });\n }\n\n /** Push an out-of-band event into the live webchat stream for a session. */\n enqueueWebchatSseEvent(\n sessionKey: string,\n event: { type: string; [key: string]: unknown },\n ): void {\n const pub = this.cfg.sessionState.getWebchatPublisher(sessionKey);\n if (pub) {\n pub(event);\n }\n }\n\n /** Stream assistant text to live webchat session + notify transcript listeners. */\n notifyWebchatTranscriptAppend(sessionKey: string, assistantText: string): void {\n const trimmed = assistantText.trim();\n if (trimmed) {\n const message = {\n role: 'assistant',\n content: [{ type: 'text', text: trimmed }],\n timestamp: Date.now(),\n };\n this.enqueueWebchatSseEvent(sessionKey, { type: 'message_start', message });\n this.enqueueWebchatSseEvent(sessionKey, { type: 'message_update', message });\n this.enqueueWebchatSseEvent(sessionKey, { type: 'message_end', message });\n }\n this.cfg.onSessionTranscriptUpdated?.(sessionKey);\n }\n\n /**\n * Queue a steering user message into pi-agent's in-flight run (delivered\n * after current tool work, before the next LLM call). See `Agent.steer`\n * in `@earendil-works/pi-agent-core`.\n */\n async steerWebchatSession(sessionKey: string, text: string): Promise<boolean> {\n const trimmed = text.trim();\n if (!trimmed) return false;\n try {\n return await queueEmbeddedSteer(sessionKey, trimmed);\n } catch (err) {\n this.log.warn({ err, sessionKey }, 'steerWebchatSession failed');\n return false;\n }\n }\n\n private buildStreamingDeps(): ProcessDirectStreamingDeps {\n const c = this.cfg;\n return {\n log: this.log,\n resolveSessionEndpoint: c.resolveSessionEndpoint,\n initDirectStreamingSession: c.initSessionContext,\n registerWebchatSsePublisher: (sk, publisher) =>\n c.sessionState.registerWebchatPublisher(sk, publisher),\n unregisterWebchatSsePublisher: (sk) => c.sessionState.unregisterWebchatPublisher(sk),\n agentManager: c.agentManager,\n hydrateSessionWorkspaceFromStore: (sk) => c.sessionHydrator.workspace(sk),\n hydrateSessionModelFromStore: (sk) => c.sessionHydrator.model(sk),\n sessionStore: c.sessionStore,\n modelManager: c.modelManager,\n applyResolvedThinkingLevel: (sk, t) => c.sessionHydrator.thinking(sk, t),\n getConfig: c.getConfig,\n sessionConfigStore: c.sessionConfigStore,\n commandHandler: c.commandHandler,\n prepareInboundAttachments: c.prepareInboundAttachments,\n buildTranscriptUserMessage: async (text, prepared, sk) =>\n buildDirectUserMessageContent({\n content: text,\n attachments: prepared,\n sessionKey: sk,\n config: c.requireConfig(),\n agentManager: c.agentManager,\n modelManager: c.modelManager,\n }),\n recordPersistentGoalStreamOutcome: (sk, o) =>\n c.sessionState.recordPersistentGoalStreamOutcome(sk, o),\n enqueueProvisionalSessionTitle: (sk, userText) => {\n c.enqueueProvisionalSessionTitle?.(sk, userText);\n },\n onTurnComplete: (sk, text) => {\n if (text) {\n c.sessionState.setLastAssistantText(sk, text);\n }\n c.enqueueMaybeAutoTitleAfterPersist(sk);\n },\n reloadWebchatTranscript: (sk) => {\n c.onSessionTranscriptUpdated?.(sk);\n },\n sourceContextResolver: c.sourceContextResolver,\n maybeEmitWebchatTts: (sk, hadVoice) =>\n maybeEmitWebchatTts(\n {\n config: c.getConfig(),\n sessionStore: c.sessionStore,\n getLastAssistantPlainText: (sessionKey) =>\n c.sessionState.getLastAssistantText(sessionKey) ??\n c.agentManager.getLastAssistantContent(sessionKey) ??\n '',\n log: this.log,\n },\n sk,\n hadVoice,\n ),\n endDirectRequestContext: c.endDirectRequestContext,\n resetSession: c.resetSession,\n };\n }\n\n private buildOneShotDeps(): RunProcessDirectDeps {\n const c = this.cfg;\n const cfg = c.requireConfig();\n return {\n log: this.log,\n config: cfg,\n resolveSessionEndpoint: c.resolveSessionEndpoint,\n initSessionContext: (sk, channel, chatId) => {\n void c.initSessionContext(sk, channel, chatId);\n },\n hydrateSessionWorkspaceFromStore: (sk) => c.sessionHydrator.workspace(sk),\n hydrateSessionModelFromStore: (sk) => c.sessionHydrator.model(sk),\n agentManager: c.agentManager,\n sessionStore: c.sessionStore,\n modelManager: c.modelManager,\n applyResolvedThinkingLevel: (sk, t) => c.sessionHydrator.thinking(sk, t),\n prepareInboundAttachments: c.prepareInboundAttachments,\n commandHandler: c.commandHandler,\n onTurnComplete: (sk, text) => {\n if (text) {\n c.sessionState.setLastAssistantText(sk, text);\n }\n c.enqueueMaybeAutoTitleAfterPersist(sk);\n },\n endDirectRequestContext: c.endDirectRequestContext,\n resetSession: c.resetSession,\n };\n }\n}\n"],"mappings":"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"}
@@ -6,12 +6,39 @@ export type McpGatewayToolEntry = {
6
6
  shortName: string;
7
7
  description: string;
8
8
  };
9
+ export type McpGatewayResourceEntry = {
10
+ uri: string;
11
+ name: string;
12
+ title?: string;
13
+ description?: string;
14
+ mimeType?: string;
15
+ };
16
+ export type McpGatewayPromptEntry = {
17
+ name: string;
18
+ title?: string;
19
+ description?: string;
20
+ argumentCount: number;
21
+ };
22
+ export type McpGatewayCapabilitySummary = {
23
+ serverId: string;
24
+ toolCount: number;
25
+ resourceCount: number;
26
+ promptCount: number;
27
+ tools: McpGatewayToolEntry[];
28
+ resources: McpGatewayResourceEntry[];
29
+ prompts: McpGatewayPromptEntry[];
30
+ };
9
31
  export declare function mapBundleMcpToolsForGateway(tools: AnyAgentTool[], serverId: string): McpGatewayToolEntry[];
10
32
  export declare function listBundleMcpServerToolsForGateway(params: {
11
33
  workspaceDir: string;
12
34
  cfg?: Config;
13
35
  serverId: string;
14
36
  }): Promise<McpGatewayToolEntry[]>;
37
+ export declare function listBundleMcpServerCapabilitiesForGateway(params: {
38
+ workspaceDir: string;
39
+ cfg?: Config;
40
+ serverId: string;
41
+ }): Promise<McpGatewayCapabilitySummary>;
15
42
  export declare function materializeBundleMcpToolsForRun(params: {
16
43
  runtime: SessionMcpRuntime;
17
44
  reservedToolNames?: Iterable<string>;
@@ -31,6 +31,54 @@ async function listBundleMcpServerToolsForGateway(params) {
31
31
  await runtime.dispose();
32
32
  }
33
33
  }
34
+ function mapBundleMcpResourcesForGateway(resources, serverId) {
35
+ return resources.filter((resource) => resource.serverName === serverId).map((resource) => ({
36
+ uri: resource.uri,
37
+ name: resource.name,
38
+ title: resource.title,
39
+ description: resource.description,
40
+ mimeType: resource.mimeType
41
+ }));
42
+ }
43
+ function mapBundleMcpPromptsForGateway(prompts, serverId) {
44
+ return prompts.filter((prompt) => prompt.serverName === serverId).map((prompt) => ({
45
+ name: prompt.name,
46
+ title: prompt.title,
47
+ description: prompt.description,
48
+ argumentCount: prompt.argumentCount
49
+ }));
50
+ }
51
+ async function listBundleMcpServerCapabilitiesForGateway(params) {
52
+ const createRuntime = (await import("./bundle-mcp-runtime.js")).createSessionMcpRuntime;
53
+ const runtime = createRuntime({
54
+ sessionId: `bundle-mcp:${crypto.randomUUID()}`,
55
+ workspaceDir: params.workspaceDir,
56
+ cfg: params.cfg
57
+ });
58
+ try {
59
+ const catalog = await runtime.getCatalog();
60
+ const tools = mapBundleMcpToolsForGateway(await materializeBundleMcpToolsForRun({ runtime }).then(async (toolRuntime) => {
61
+ try {
62
+ return toolRuntime.tools;
63
+ } finally {
64
+ await toolRuntime.dispose();
65
+ }
66
+ }), params.serverId);
67
+ const resources = mapBundleMcpResourcesForGateway(catalog.resources, params.serverId);
68
+ const prompts = mapBundleMcpPromptsForGateway(catalog.prompts, params.serverId);
69
+ return {
70
+ serverId: params.serverId,
71
+ toolCount: tools.length,
72
+ resourceCount: resources.length,
73
+ promptCount: prompts.length,
74
+ tools,
75
+ resources,
76
+ prompts
77
+ };
78
+ } finally {
79
+ await runtime.dispose();
80
+ }
81
+ }
34
82
  function toAgentToolResult(params) {
35
83
  const content = Array.isArray(params.result.content) ? params.result.content : [];
36
84
  const normalizedContent = content.length > 0 ? content : params.result.structuredContent !== void 0 ? [{
@@ -132,6 +180,6 @@ async function createBundleMcpToolRuntime(params) {
132
180
  });
133
181
  }
134
182
  //#endregion
135
- export { createBundleMcpToolRuntime, listBundleMcpServerToolsForGateway, mapBundleMcpToolsForGateway, materializeBundleMcpToolsForRun };
183
+ export { createBundleMcpToolRuntime, listBundleMcpServerCapabilitiesForGateway, listBundleMcpServerToolsForGateway, mapBundleMcpToolsForGateway, materializeBundleMcpToolsForRun };
136
184
 
137
185
  //# sourceMappingURL=bundle-mcp-materialize.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bundle-mcp-materialize.js","names":[],"sources":["../../../../src/agent/mcp/bundle-mcp-materialize.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport type { AgentToolResult } from '@earendil-works/pi-agent-core';\nimport type { AgentTool as AnyAgentTool } from \"@earendil-works/pi-agent-core\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport type { Config } from \"../../config/schema.js\";\nimport { createLogger } from \"../../utils/logger.js\";\nconst log = createLogger(\"Mcp:Bundle\");\nimport { setPluginToolMeta } from \"./mcp-tool-meta.js\";\nimport {\n normalizeLowercaseStringOrEmpty,\n normalizeOptionalString,\n} from \"../../utils/string-coerce.js\";\nimport {\n buildSafeToolName,\n normalizeReservedToolNames,\n TOOL_NAME_SEPARATOR,\n} from \"./bundle-mcp-names.js\";\nimport type { BundleMcpToolRuntime, SessionMcpRuntime } from \"./bundle-mcp-types.js\";\n\nexport type McpGatewayToolEntry = {\n name: string;\n shortName: string;\n description: string;\n};\n\nfunction readMaterializedToolDescription(tool: AnyAgentTool): string {\n const raw = tool as { description?: unknown; label?: unknown };\n return (\n normalizeOptionalString(raw.description) ??\n normalizeOptionalString(raw.label) ??\n tool.name\n );\n}\n\nexport function mapBundleMcpToolsForGateway(\n tools: AnyAgentTool[],\n serverId: string,\n): McpGatewayToolEntry[] {\n const prefix = `${serverId.trim()}${TOOL_NAME_SEPARATOR}`;\n return tools\n .filter((tool) => tool.name.startsWith(prefix))\n .map((tool) => ({\n name: tool.name,\n shortName: tool.name.slice(prefix.length),\n description: readMaterializedToolDescription(tool),\n }));\n}\n\nexport async function listBundleMcpServerToolsForGateway(params: {\n workspaceDir: string;\n cfg?: Config;\n serverId: string;\n}): Promise<McpGatewayToolEntry[]> {\n const runtime = await createBundleMcpToolRuntime({\n workspaceDir: params.workspaceDir,\n cfg: params.cfg,\n });\n try {\n return mapBundleMcpToolsForGateway(runtime.tools, params.serverId);\n } finally {\n await runtime.dispose();\n }\n}\n\nfunction toAgentToolResult(params: {\n serverName: string;\n toolName: string;\n result: CallToolResult;\n}): AgentToolResult<unknown> {\n const content = Array.isArray(params.result.content)\n ? (params.result.content as AgentToolResult<unknown>[\"content\"])\n : [];\n const normalizedContent: AgentToolResult<unknown>[\"content\"] =\n content.length > 0\n ? content\n : params.result.structuredContent !== undefined\n ? [\n {\n type: \"text\",\n text: JSON.stringify(params.result.structuredContent, null, 2),\n },\n ]\n : ([\n {\n type: \"text\",\n text: JSON.stringify(\n {\n status: params.result.isError === true ? \"error\" : \"ok\",\n server: params.serverName,\n tool: params.toolName,\n },\n null,\n 2,\n ),\n },\n ] as AgentToolResult<unknown>[\"content\"]);\n const details: Record<string, unknown> = {\n mcpServer: params.serverName,\n mcpTool: params.toolName,\n };\n if (params.result.structuredContent !== undefined) {\n details.structuredContent = params.result.structuredContent;\n }\n if (params.result.isError === true) {\n details.status = \"error\";\n }\n return {\n content: normalizedContent,\n details,\n };\n}\n\nexport async function materializeBundleMcpToolsForRun(params: {\n runtime: SessionMcpRuntime;\n reservedToolNames?: Iterable<string>;\n disposeRuntime?: () => Promise<void>;\n}): Promise<BundleMcpToolRuntime> {\n let disposed = false;\n const releaseLease = params.runtime.acquireLease?.();\n params.runtime.markUsed();\n let catalog;\n try {\n catalog = await params.runtime.getCatalog();\n } catch (error) {\n releaseLease?.();\n throw error;\n }\n const reservedNames = normalizeReservedToolNames(params.reservedToolNames);\n const tools: BundleMcpToolRuntime[\"tools\"] = [];\n const sortedCatalogTools = [...catalog.tools].toSorted((a, b) => {\n const serverOrder = a.safeServerName.localeCompare(b.safeServerName);\n if (serverOrder !== 0) {\n return serverOrder;\n }\n const toolOrder = a.toolName.localeCompare(b.toolName);\n if (toolOrder !== 0) {\n return toolOrder;\n }\n return a.serverName.localeCompare(b.serverName);\n });\n\n for (const tool of sortedCatalogTools) {\n const originalName = tool.toolName.trim();\n if (!originalName) {\n continue;\n }\n const safeToolName = buildSafeToolName({\n serverName: tool.safeServerName,\n toolName: originalName,\n reservedNames,\n });\n if (safeToolName !== `${tool.safeServerName}${TOOL_NAME_SEPARATOR}${originalName}`) {\n log.warn(\n `bundle-mcp: tool \"${tool.toolName}\" from server \"${tool.serverName}\" registered as \"${safeToolName}\" to keep the tool name provider-safe.`,\n );\n }\n reservedNames.add(normalizeLowercaseStringOrEmpty(safeToolName));\n const agentTool = {\n name: safeToolName,\n label: tool.title ?? tool.toolName,\n description: tool.description || tool.title || tool.fallbackDescription,\n parameters: tool.inputSchema,\n execute: async (_toolCallId: string, input: unknown) => {\n params.runtime.markUsed();\n const result = await params.runtime.callTool(tool.serverName, tool.toolName, input);\n return toAgentToolResult({\n serverName: tool.serverName,\n toolName: tool.toolName,\n result,\n });\n },\n } as unknown as AnyAgentTool;\n setPluginToolMeta(agentTool, {\n pluginId: \"bundle-mcp\",\n optional: false,\n });\n tools.push(agentTool);\n }\n\n // Sort tools deterministically by name so the tools block in API requests is stable across\n // turns (defensive — listTools() order is usually stable but not guaranteed).\n // Cannot fix name collisions: collision suffixes above are order-dependent.\n tools.sort((a, b) => a.name.localeCompare(b.name));\n\n return {\n tools,\n dispose: async () => {\n if (disposed) {\n return;\n }\n disposed = true;\n releaseLease?.();\n await params.disposeRuntime?.();\n },\n };\n}\n\nexport async function createBundleMcpToolRuntime(params: {\n workspaceDir: string;\n cfg?: Config;\n reservedToolNames?: Iterable<string>;\n createRuntime?: (params: {\n sessionId: string;\n workspaceDir: string;\n cfg?: Config;\n }) => SessionMcpRuntime;\n}): Promise<BundleMcpToolRuntime> {\n const createRuntime =\n params.createRuntime ?? (await import(\"./bundle-mcp-runtime.js\")).createSessionMcpRuntime;\n const runtime = createRuntime({\n sessionId: `bundle-mcp:${crypto.randomUUID()}`,\n workspaceDir: params.workspaceDir,\n cfg: params.cfg,\n });\n const materialized = await materializeBundleMcpToolsForRun({\n runtime,\n reservedToolNames: params.reservedToolNames,\n disposeRuntime: async () => {\n await runtime.dispose();\n },\n });\n return materialized;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"bundle-mcp-materialize.js","names":[],"sources":["../../../../src/agent/mcp/bundle-mcp-materialize.ts"],"sourcesContent":["import crypto from \"node:crypto\";\nimport type { AgentToolResult } from '@earendil-works/pi-agent-core';\nimport type { AgentTool as AnyAgentTool } from \"@earendil-works/pi-agent-core\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport type { Config } from \"../../config/schema.js\";\nimport { createLogger } from \"../../utils/logger.js\";\nconst log = createLogger(\"Mcp:Bundle\");\nimport { setPluginToolMeta } from \"./mcp-tool-meta.js\";\nimport {\n normalizeLowercaseStringOrEmpty,\n normalizeOptionalString,\n} from \"../../utils/string-coerce.js\";\nimport {\n buildSafeToolName,\n normalizeReservedToolNames,\n TOOL_NAME_SEPARATOR,\n} from \"./bundle-mcp-names.js\";\nimport type { BundleMcpToolRuntime, SessionMcpRuntime } from \"./bundle-mcp-types.js\";\nimport type { McpCatalogPrompt, McpCatalogResource } from \"./bundle-mcp-types.js\";\n\nexport type McpGatewayToolEntry = {\n name: string;\n shortName: string;\n description: string;\n};\n\nexport type McpGatewayResourceEntry = {\n uri: string;\n name: string;\n title?: string;\n description?: string;\n mimeType?: string;\n};\n\nexport type McpGatewayPromptEntry = {\n name: string;\n title?: string;\n description?: string;\n argumentCount: number;\n};\n\nexport type McpGatewayCapabilitySummary = {\n serverId: string;\n toolCount: number;\n resourceCount: number;\n promptCount: number;\n tools: McpGatewayToolEntry[];\n resources: McpGatewayResourceEntry[];\n prompts: McpGatewayPromptEntry[];\n};\n\nfunction readMaterializedToolDescription(tool: AnyAgentTool): string {\n const raw = tool as { description?: unknown; label?: unknown };\n return (\n normalizeOptionalString(raw.description) ??\n normalizeOptionalString(raw.label) ??\n tool.name\n );\n}\n\nexport function mapBundleMcpToolsForGateway(\n tools: AnyAgentTool[],\n serverId: string,\n): McpGatewayToolEntry[] {\n const prefix = `${serverId.trim()}${TOOL_NAME_SEPARATOR}`;\n return tools\n .filter((tool) => tool.name.startsWith(prefix))\n .map((tool) => ({\n name: tool.name,\n shortName: tool.name.slice(prefix.length),\n description: readMaterializedToolDescription(tool),\n }));\n}\n\nexport async function listBundleMcpServerToolsForGateway(params: {\n workspaceDir: string;\n cfg?: Config;\n serverId: string;\n}): Promise<McpGatewayToolEntry[]> {\n const runtime = await createBundleMcpToolRuntime({\n workspaceDir: params.workspaceDir,\n cfg: params.cfg,\n });\n try {\n return mapBundleMcpToolsForGateway(runtime.tools, params.serverId);\n } finally {\n await runtime.dispose();\n }\n}\n\nfunction mapBundleMcpResourcesForGateway(\n resources: McpCatalogResource[],\n serverId: string,\n): McpGatewayResourceEntry[] {\n return resources\n .filter((resource) => resource.serverName === serverId)\n .map((resource) => ({\n uri: resource.uri,\n name: resource.name,\n title: resource.title,\n description: resource.description,\n mimeType: resource.mimeType,\n }));\n}\n\nfunction mapBundleMcpPromptsForGateway(\n prompts: McpCatalogPrompt[],\n serverId: string,\n): McpGatewayPromptEntry[] {\n return prompts\n .filter((prompt) => prompt.serverName === serverId)\n .map((prompt) => ({\n name: prompt.name,\n title: prompt.title,\n description: prompt.description,\n argumentCount: prompt.argumentCount,\n }));\n}\n\nexport async function listBundleMcpServerCapabilitiesForGateway(params: {\n workspaceDir: string;\n cfg?: Config;\n serverId: string;\n}): Promise<McpGatewayCapabilitySummary> {\n const createRuntime =\n (await import(\"./bundle-mcp-runtime.js\")).createSessionMcpRuntime;\n const runtime = createRuntime({\n sessionId: `bundle-mcp:${crypto.randomUUID()}`,\n workspaceDir: params.workspaceDir,\n cfg: params.cfg,\n });\n try {\n const catalog = await runtime.getCatalog();\n const tools = mapBundleMcpToolsForGateway(\n await materializeBundleMcpToolsForRun({ runtime }).then(async (toolRuntime) => {\n try {\n return toolRuntime.tools;\n } finally {\n await toolRuntime.dispose();\n }\n }),\n params.serverId,\n );\n const resources = mapBundleMcpResourcesForGateway(catalog.resources, params.serverId);\n const prompts = mapBundleMcpPromptsForGateway(catalog.prompts, params.serverId);\n return {\n serverId: params.serverId,\n toolCount: tools.length,\n resourceCount: resources.length,\n promptCount: prompts.length,\n tools,\n resources,\n prompts,\n };\n } finally {\n await runtime.dispose();\n }\n}\n\nfunction toAgentToolResult(params: {\n serverName: string;\n toolName: string;\n result: CallToolResult;\n}): AgentToolResult<unknown> {\n const content = Array.isArray(params.result.content)\n ? (params.result.content as AgentToolResult<unknown>[\"content\"])\n : [];\n const normalizedContent: AgentToolResult<unknown>[\"content\"] =\n content.length > 0\n ? content\n : params.result.structuredContent !== undefined\n ? [\n {\n type: \"text\",\n text: JSON.stringify(params.result.structuredContent, null, 2),\n },\n ]\n : ([\n {\n type: \"text\",\n text: JSON.stringify(\n {\n status: params.result.isError === true ? \"error\" : \"ok\",\n server: params.serverName,\n tool: params.toolName,\n },\n null,\n 2,\n ),\n },\n ] as AgentToolResult<unknown>[\"content\"]);\n const details: Record<string, unknown> = {\n mcpServer: params.serverName,\n mcpTool: params.toolName,\n };\n if (params.result.structuredContent !== undefined) {\n details.structuredContent = params.result.structuredContent;\n }\n if (params.result.isError === true) {\n details.status = \"error\";\n }\n return {\n content: normalizedContent,\n details,\n };\n}\n\nexport async function materializeBundleMcpToolsForRun(params: {\n runtime: SessionMcpRuntime;\n reservedToolNames?: Iterable<string>;\n disposeRuntime?: () => Promise<void>;\n}): Promise<BundleMcpToolRuntime> {\n let disposed = false;\n const releaseLease = params.runtime.acquireLease?.();\n params.runtime.markUsed();\n let catalog;\n try {\n catalog = await params.runtime.getCatalog();\n } catch (error) {\n releaseLease?.();\n throw error;\n }\n const reservedNames = normalizeReservedToolNames(params.reservedToolNames);\n const tools: BundleMcpToolRuntime[\"tools\"] = [];\n const sortedCatalogTools = [...catalog.tools].toSorted((a, b) => {\n const serverOrder = a.safeServerName.localeCompare(b.safeServerName);\n if (serverOrder !== 0) {\n return serverOrder;\n }\n const toolOrder = a.toolName.localeCompare(b.toolName);\n if (toolOrder !== 0) {\n return toolOrder;\n }\n return a.serverName.localeCompare(b.serverName);\n });\n\n for (const tool of sortedCatalogTools) {\n const originalName = tool.toolName.trim();\n if (!originalName) {\n continue;\n }\n const safeToolName = buildSafeToolName({\n serverName: tool.safeServerName,\n toolName: originalName,\n reservedNames,\n });\n if (safeToolName !== `${tool.safeServerName}${TOOL_NAME_SEPARATOR}${originalName}`) {\n log.warn(\n `bundle-mcp: tool \"${tool.toolName}\" from server \"${tool.serverName}\" registered as \"${safeToolName}\" to keep the tool name provider-safe.`,\n );\n }\n reservedNames.add(normalizeLowercaseStringOrEmpty(safeToolName));\n const agentTool = {\n name: safeToolName,\n label: tool.title ?? tool.toolName,\n description: tool.description || tool.title || tool.fallbackDescription,\n parameters: tool.inputSchema,\n execute: async (_toolCallId: string, input: unknown) => {\n params.runtime.markUsed();\n const result = await params.runtime.callTool(tool.serverName, tool.toolName, input);\n return toAgentToolResult({\n serverName: tool.serverName,\n toolName: tool.toolName,\n result,\n });\n },\n } as unknown as AnyAgentTool;\n setPluginToolMeta(agentTool, {\n pluginId: \"bundle-mcp\",\n optional: false,\n });\n tools.push(agentTool);\n }\n\n // Sort tools deterministically by name so the tools block in API requests is stable across\n // turns (defensive — listTools() order is usually stable but not guaranteed).\n // Cannot fix name collisions: collision suffixes above are order-dependent.\n tools.sort((a, b) => a.name.localeCompare(b.name));\n\n return {\n tools,\n dispose: async () => {\n if (disposed) {\n return;\n }\n disposed = true;\n releaseLease?.();\n await params.disposeRuntime?.();\n },\n };\n}\n\nexport async function createBundleMcpToolRuntime(params: {\n workspaceDir: string;\n cfg?: Config;\n reservedToolNames?: Iterable<string>;\n createRuntime?: (params: {\n sessionId: string;\n workspaceDir: string;\n cfg?: Config;\n }) => SessionMcpRuntime;\n}): Promise<BundleMcpToolRuntime> {\n const createRuntime =\n params.createRuntime ?? (await import(\"./bundle-mcp-runtime.js\")).createSessionMcpRuntime;\n const runtime = createRuntime({\n sessionId: `bundle-mcp:${crypto.randomUUID()}`,\n workspaceDir: params.workspaceDir,\n cfg: params.cfg,\n });\n const materialized = await materializeBundleMcpToolsForRun({\n runtime,\n reservedToolNames: params.reservedToolNames,\n disposeRuntime: async () => {\n await runtime.dispose();\n },\n });\n return materialized;\n}\n"],"mappings":"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"}
@@ -73,6 +73,39 @@ async function listAllTools(client) {
73
73
  } while (cursor);
74
74
  return tools;
75
75
  }
76
+ async function listAllResources(client) {
77
+ const resources = [];
78
+ let cursor;
79
+ do {
80
+ const page = await client.listResources(cursor ? { cursor } : void 0);
81
+ resources.push(...page.resources);
82
+ cursor = page.nextCursor;
83
+ } while (cursor);
84
+ return resources;
85
+ }
86
+ async function listAllPrompts(client) {
87
+ const prompts = [];
88
+ let cursor;
89
+ do {
90
+ const page = await client.listPrompts(cursor ? { cursor } : void 0);
91
+ prompts.push(...page.prompts);
92
+ cursor = page.nextCursor;
93
+ } while (cursor);
94
+ return prompts;
95
+ }
96
+ async function listOptionalMcpCapability(params) {
97
+ try {
98
+ return await params.list();
99
+ } catch (error) {
100
+ log.debug({
101
+ phase: "agent.mcp_connect",
102
+ serverName: params.serverName,
103
+ capability: params.capability,
104
+ errorMessage: redactErrorUrls(error)
105
+ }, `bundle-mcp: server "${params.serverName}" did not list ${params.capability}`);
106
+ return [];
107
+ }
108
+ }
76
109
  async function disposeSession(session) {
77
110
  session.detachStderr?.();
78
111
  if (session.transportType === "streamable-http") await session.transport.terminateSession().catch(() => {});
@@ -130,10 +163,14 @@ function createSessionMcpRuntime(params) {
130
163
  version: 1,
131
164
  generatedAt: Date.now(),
132
165
  servers: {},
133
- tools: []
166
+ tools: [],
167
+ resources: [],
168
+ prompts: []
134
169
  };
135
170
  const servers = {};
136
171
  const tools = [];
172
+ const resources = [];
173
+ const prompts = [];
137
174
  const usedServerNames = /* @__PURE__ */ new Set();
138
175
  try {
139
176
  for (const [serverName, rawServer] of Object.entries(loaded.mcpServers)) {
@@ -163,11 +200,23 @@ function createSessionMcpRuntime(params) {
163
200
  await connectWithTimeout(client, resolved.transport, resolved.connectionTimeoutMs);
164
201
  failIfDisposed();
165
202
  const listedTools = await listAllTools(client);
203
+ const listedResources = await listOptionalMcpCapability({
204
+ serverName,
205
+ capability: "resources",
206
+ list: () => listAllResources(client)
207
+ });
208
+ const listedPrompts = await listOptionalMcpCapability({
209
+ serverName,
210
+ capability: "prompts",
211
+ list: () => listAllPrompts(client)
212
+ });
166
213
  failIfDisposed();
167
214
  servers[serverName] = {
168
215
  serverName,
169
216
  launchSummary: resolved.description,
170
- toolCount: listedTools.length
217
+ toolCount: listedTools.length,
218
+ resourceCount: listedResources.length,
219
+ promptCount: listedPrompts.length
171
220
  };
172
221
  for (const tool of listedTools) {
173
222
  const toolName = tool.name.trim();
@@ -182,6 +231,30 @@ function createSessionMcpRuntime(params) {
182
231
  fallbackDescription: `Provided by bundle MCP server "${serverName}" (${resolved.description}).`
183
232
  });
184
233
  }
234
+ for (const resource of listedResources) {
235
+ const uri = resource.uri.trim();
236
+ const name = resource.name.trim();
237
+ if (!uri || !name) continue;
238
+ resources.push({
239
+ serverName,
240
+ uri,
241
+ name,
242
+ title: resource.title,
243
+ description: normalizeOptionalString(resource.description),
244
+ mimeType: normalizeOptionalString(resource.mimeType)
245
+ });
246
+ }
247
+ for (const prompt of listedPrompts) {
248
+ const name = prompt.name.trim();
249
+ if (!name) continue;
250
+ prompts.push({
251
+ serverName,
252
+ name,
253
+ title: prompt.title,
254
+ description: normalizeOptionalString(prompt.description),
255
+ argumentCount: Array.isArray(prompt.arguments) ? prompt.arguments.length : 0
256
+ });
257
+ }
185
258
  } catch (error) {
186
259
  if (!disposed) log.warn(`bundle-mcp: failed to start server "${serverName}" (${resolved.description}): ${redactErrorUrls(error)}`);
187
260
  await disposeSession(session);
@@ -194,7 +267,9 @@ function createSessionMcpRuntime(params) {
194
267
  version: 1,
195
268
  generatedAt: Date.now(),
196
269
  servers,
197
- tools
270
+ tools,
271
+ resources,
272
+ prompts
198
273
  };
199
274
  } catch (error) {
200
275
  await Promise.allSettled(Array.from(sessions.values(), (session) => disposeSession(session)));