@xopcai/xopc 0.0.98 → 0.0.100

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 (515) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
  3. package/dist/extensions/feishu/src/workflow-progress.js +1 -1
  4. package/dist/extensions/telegram/src/account-manager.d.ts +3 -3
  5. package/dist/extensions/telegram/src/account-manager.js +8 -8
  6. package/dist/extensions/telegram/src/account-manager.js.map +1 -1
  7. package/dist/extensions/telegram/src/actions/message-actions.d.ts +4 -0
  8. package/dist/extensions/telegram/src/actions/message-actions.js +71 -0
  9. package/dist/extensions/telegram/src/actions/message-actions.js.map +1 -0
  10. package/dist/extensions/telegram/src/adapters/config-adapter.js +9 -1
  11. package/dist/extensions/telegram/src/adapters/config-adapter.js.map +1 -1
  12. package/dist/extensions/telegram/src/adapters/config-surface.js +1 -1
  13. package/dist/extensions/telegram/src/adapters/config-surface.js.map +1 -1
  14. package/dist/extensions/telegram/src/adapters/outbound-adapter.d.ts +2 -1
  15. package/dist/extensions/telegram/src/adapters/outbound-adapter.js +18 -4
  16. package/dist/extensions/telegram/src/adapters/outbound-adapter.js.map +1 -1
  17. package/dist/extensions/telegram/src/adapters/security-adapter.js +2 -1
  18. package/dist/extensions/telegram/src/adapters/security-adapter.js.map +1 -1
  19. package/dist/extensions/telegram/src/adapters/streaming-adapter.js +90 -6
  20. package/dist/extensions/telegram/src/adapters/streaming-adapter.js.map +1 -1
  21. package/dist/extensions/telegram/src/adapters/types.d.ts +9 -1
  22. package/dist/extensions/telegram/src/api-root.d.ts +4 -0
  23. package/dist/extensions/telegram/src/api-root.js +42 -0
  24. package/dist/extensions/telegram/src/api-root.js.map +1 -0
  25. package/dist/extensions/telegram/src/approval-delivery.d.ts +9 -0
  26. package/dist/extensions/telegram/src/approval-delivery.js +36 -0
  27. package/dist/extensions/telegram/src/approval-delivery.js.map +1 -0
  28. package/dist/extensions/telegram/src/approval-store.d.ts +25 -0
  29. package/dist/extensions/telegram/src/approval-store.js +61 -0
  30. package/dist/extensions/telegram/src/approval-store.js.map +1 -0
  31. package/dist/extensions/telegram/src/config-schema.d.ts +185 -35
  32. package/dist/extensions/telegram/src/config-schema.js +67 -45
  33. package/dist/extensions/telegram/src/config-schema.js.map +1 -1
  34. package/dist/extensions/telegram/src/conversation-id.d.ts +6 -0
  35. package/dist/extensions/telegram/src/conversation-id.js +18 -0
  36. package/dist/extensions/telegram/src/conversation-id.js.map +1 -0
  37. package/dist/extensions/telegram/src/doctor.d.ts +5 -0
  38. package/dist/extensions/telegram/src/doctor.js +80 -0
  39. package/dist/extensions/telegram/src/doctor.js.map +1 -0
  40. package/dist/extensions/telegram/src/exec-approval-handler.d.ts +7 -0
  41. package/dist/extensions/telegram/src/exec-approval-handler.js +36 -0
  42. package/dist/extensions/telegram/src/exec-approval-handler.js.map +1 -0
  43. package/dist/extensions/telegram/src/exec-approvals.d.ts +8 -0
  44. package/dist/extensions/telegram/src/exec-approvals.js +20 -0
  45. package/dist/extensions/telegram/src/exec-approvals.js.map +1 -0
  46. package/dist/extensions/telegram/src/focus-handler.d.ts +12 -0
  47. package/dist/extensions/telegram/src/focus-handler.js +55 -0
  48. package/dist/extensions/telegram/src/focus-handler.js.map +1 -0
  49. package/dist/extensions/telegram/src/group-config-resolver.d.ts +12 -0
  50. package/dist/extensions/telegram/src/group-config-resolver.js +17 -0
  51. package/dist/extensions/telegram/src/group-config-resolver.js.map +1 -0
  52. package/dist/extensions/telegram/src/inbound-coalescer.d.ts +15 -0
  53. package/dist/extensions/telegram/src/inbound-coalescer.js +107 -0
  54. package/dist/extensions/telegram/src/inbound-coalescer.js.map +1 -0
  55. package/dist/extensions/telegram/src/inbound-processor.d.ts +1 -0
  56. package/dist/extensions/telegram/src/inbound-processor.js +41 -4
  57. package/dist/extensions/telegram/src/inbound-processor.js.map +1 -1
  58. package/dist/extensions/telegram/src/network-errors.d.ts +2 -0
  59. package/dist/extensions/telegram/src/network-errors.js +23 -0
  60. package/dist/extensions/telegram/src/network-errors.js.map +1 -0
  61. package/dist/extensions/telegram/src/plugin.d.ts +11 -0
  62. package/dist/extensions/telegram/src/plugin.js +192 -58
  63. package/dist/extensions/telegram/src/plugin.js.map +1 -1
  64. package/dist/extensions/telegram/src/polling-session.d.ts +17 -0
  65. package/dist/extensions/telegram/src/polling-session.js +112 -0
  66. package/dist/extensions/telegram/src/polling-session.js.map +1 -0
  67. package/dist/extensions/telegram/src/proxy-fetch.d.ts +2 -0
  68. package/dist/extensions/telegram/src/proxy-fetch.js +14 -0
  69. package/dist/extensions/telegram/src/proxy-fetch.js.map +1 -0
  70. package/dist/extensions/telegram/src/reactions.d.ts +14 -0
  71. package/dist/extensions/telegram/src/reactions.js +48 -0
  72. package/dist/extensions/telegram/src/reactions.js.map +1 -0
  73. package/dist/extensions/telegram/src/reply-params.d.ts +15 -0
  74. package/dist/extensions/telegram/src/reply-params.js +28 -0
  75. package/dist/extensions/telegram/src/reply-params.js.map +1 -0
  76. package/dist/extensions/telegram/src/routing-integration.js +2 -2
  77. package/dist/extensions/telegram/src/startup-errors.d.ts +3 -0
  78. package/dist/extensions/telegram/src/startup-errors.js +27 -0
  79. package/dist/extensions/telegram/src/startup-errors.js.map +1 -0
  80. package/dist/extensions/telegram/src/telegram-actions.d.ts +25 -0
  81. package/dist/extensions/telegram/src/telegram-actions.js +65 -0
  82. package/dist/extensions/telegram/src/telegram-actions.js.map +1 -0
  83. package/dist/extensions/telegram/src/thread-bindings.d.ts +15 -0
  84. package/dist/extensions/telegram/src/thread-bindings.js +22 -0
  85. package/dist/extensions/telegram/src/thread-bindings.js.map +1 -0
  86. package/dist/extensions/telegram/src/token-resolver.d.ts +8 -0
  87. package/dist/extensions/telegram/src/token-resolver.js +34 -0
  88. package/dist/extensions/telegram/src/token-resolver.js.map +1 -0
  89. package/dist/extensions/telegram/src/update-offset-store.d.ts +9 -0
  90. package/dist/extensions/telegram/src/update-offset-store.js +66 -0
  91. package/dist/extensions/telegram/src/update-offset-store.js.map +1 -0
  92. package/dist/extensions/telegram/src/workflow-progress.js +1 -1
  93. package/dist/extensions/telegram/xopc.extension.json +1 -1
  94. package/dist/extensions/weixin/src/api/api.js +2 -2
  95. package/dist/extensions/weixin/src/auth/accounts.js +1 -1
  96. package/dist/extensions/weixin/src/cdn/upload.js +1 -1
  97. package/dist/extensions/weixin/src/media/data-url.js +1 -1
  98. package/dist/extensions/weixin/src/messaging/debug-mode.js +1 -1
  99. package/dist/extensions/weixin/src/messaging/inbound.js +1 -1
  100. package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
  101. package/dist/extensions/weixin/src/plugin.js +1 -1
  102. package/dist/extensions/weixin/src/storage/sync-buf.js +1 -1
  103. package/dist/extensions/weixin/src/workflow-progress.js +1 -1
  104. package/dist/gateway/static/root/assets/{agents-NI7FFR-h.js → agents-C5jtBK-H.js} +1 -1
  105. package/dist/gateway/static/root/assets/{apps-page-BB4iSCrJ.js → apps-page-smJl-qf_.js} +1 -1
  106. package/dist/gateway/static/root/assets/channels-settings-CH6KgFK6.js +1 -0
  107. package/dist/gateway/static/root/assets/channels-status-swr-CV70xvY7.js +8 -0
  108. package/dist/gateway/static/root/assets/{cron-api-1dHoVFEA.js → cron-api-CgBO8B7I.js} +1 -1
  109. package/dist/gateway/static/root/assets/{cron-page-CVX6FPfb.js → cron-page-Be0cW1cR.js} +1 -1
  110. package/dist/gateway/static/root/assets/{dist-BZnl4G4p.js → dist-P8HRGUuh.js} +1 -1
  111. package/dist/gateway/static/root/assets/{extension-debug-page-DwQp_4Qv.js → extension-debug-page-BowuycYu.js} +1 -1
  112. package/dist/gateway/static/root/assets/{extension-page-l9crxSvM.js → extension-page-27-Zpv8O.js} +1 -1
  113. package/dist/gateway/static/root/assets/{extension-settings-page-CXzLbq9B.js → extension-settings-page-DDFa_rIq.js} +1 -1
  114. package/dist/gateway/static/root/assets/{fetch-BLLOP2CM.js → fetch-drJUxxKP.js} +1 -1
  115. package/dist/gateway/static/root/assets/{field-primitives-BRnEyxSl.js → field-primitives-BWLssxEb.js} +1 -1
  116. package/dist/gateway/static/root/assets/{heartbeat-config-api-eMo0k381.js → heartbeat-config-api-BVuYNYT_.js} +1 -1
  117. package/dist/gateway/static/root/assets/index-CRDAmr3W.js +4840 -0
  118. package/dist/gateway/static/root/assets/index-DjNDHzwG.css +1 -0
  119. package/dist/gateway/static/root/assets/{logs-page-BvTEevBZ.js → logs-page-BDX8IrDQ.js} +1 -1
  120. package/dist/gateway/static/root/assets/{note-detail-page-Dl3jhcWg.js → note-detail-page-BmQ9KeiA.js} +1 -1
  121. package/dist/gateway/static/root/assets/{note-time-D8Tlyotb.js → note-time-14I9Sgqh.js} +1 -1
  122. package/dist/gateway/static/root/assets/{notes-page-BWILOitx.js → notes-page-BMfP0FFf.js} +1 -1
  123. package/dist/gateway/static/root/assets/{sessions-page-3EJRugZg.js → sessions-page-C3scyChq.js} +1 -1
  124. package/dist/gateway/static/root/assets/{settings-advanced-gate-CZ5wlM_q.js → settings-advanced-gate-2ODuCjHP.js} +1 -1
  125. package/dist/gateway/static/root/assets/{settings-form-section-C6RYGnKK.js → settings-form-section-DUoGKAiW.js} +1 -1
  126. package/dist/gateway/static/root/assets/{settings-page-0egNvAMr.js → settings-page-CttAfOWV.js} +1 -1
  127. package/dist/gateway/static/root/assets/{share-preview-page-BVJtaSoK.js → share-preview-page-B77xqiiI.js} +1 -1
  128. package/dist/gateway/static/root/assets/{skills-page-DVnRQ5Mo.js → skills-page-B_bAZGzB.js} +1 -1
  129. package/dist/gateway/static/root/assets/{theme-store-e2q2yjs4.js → theme-store-BgCPrmxU.js} +1 -1
  130. package/dist/gateway/static/root/assets/url-q28QhI7l.js +3 -0
  131. package/dist/gateway/static/root/assets/{utils-Bl-94YzI.js → utils-CCGejlcX.js} +1 -1
  132. package/dist/gateway/static/root/assets/{voice-api-key-field-B53JA8qW.js → voice-api-key-field-DZzzihcY.js} +1 -1
  133. package/dist/gateway/static/root/assets/{workflow-page.utils-ontUT7yL.js → workflow-page.utils-D5RMAmT4.js} +1 -1
  134. package/dist/gateway/static/root/assets/{workflows-page-Bz7YRTdp.js → workflows-page-DEYpC6Bv.js} +1 -1
  135. package/dist/gateway/static/root/index.html +5 -5
  136. package/dist/package.js +1 -1
  137. package/dist/src/agent/agent-manager.d.ts +4 -0
  138. package/dist/src/agent/agent-manager.js +75 -11
  139. package/dist/src/agent/agent-manager.js.map +1 -1
  140. package/dist/src/agent/agent-scope.js +1 -1
  141. package/dist/src/agent/bootstrap/load-bootstrap-files.js +1 -1
  142. package/dist/src/agent/child-agent-factory.d.ts +1 -0
  143. package/dist/src/agent/child-agent-factory.js +13 -10
  144. package/dist/src/agent/child-agent-factory.js.map +1 -1
  145. package/dist/src/agent/context/workspace-seed.js +2 -2
  146. package/dist/src/agent/context/workspace-state.js +1 -1
  147. package/dist/src/agent/embedded/run-for-session.d.ts +1 -0
  148. package/dist/src/agent/embedded/run-for-session.js +1 -0
  149. package/dist/src/agent/embedded/run-for-session.js.map +1 -1
  150. package/dist/src/agent/embedded/run-turn.js +1 -17
  151. package/dist/src/agent/embedded/run-turn.js.map +1 -1
  152. package/dist/src/agent/embedded/session-runner.js +3 -1
  153. package/dist/src/agent/embedded/session-runner.js.map +1 -1
  154. package/dist/src/agent/embedded/session-tool-result-guard.d.ts +1 -0
  155. package/dist/src/agent/embedded/session-tool-result-guard.js +1 -0
  156. package/dist/src/agent/embedded/session-tool-result-guard.js.map +1 -1
  157. package/dist/src/agent/goals/goal-run-store.js +4 -4
  158. package/dist/src/agent/goals/persistent-goal-service.js +1 -1
  159. package/dist/src/agent/goals/post-turn.js +2 -2
  160. package/dist/src/agent/image/load-image-media.js +2 -2
  161. package/dist/src/agent/inbound/attachment-pipeline.d.ts +37 -0
  162. package/dist/src/agent/inbound/attachment-pipeline.js +169 -0
  163. package/dist/src/agent/inbound/attachment-pipeline.js.map +1 -0
  164. package/dist/src/agent/inbound/turn-dispatcher.d.ts +3 -5
  165. package/dist/src/agent/inbound/turn-dispatcher.js +1 -2
  166. package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
  167. package/dist/src/agent/ipc/bus.js +1 -1
  168. package/dist/src/agent/ipc/inbox.js +2 -2
  169. package/dist/src/agent/ipc/socket.js +1 -1
  170. package/dist/src/agent/mcp/bundle-mcp-materialize.js +1 -1
  171. package/dist/src/agent/mcp/bundle-mcp-runtime.js +1 -1
  172. package/dist/src/agent/mcp/mcp-transport-config.js +1 -1
  173. package/dist/src/agent/mcp/mcp-transport.js +1 -1
  174. package/dist/src/agent/memory/builtin-memory-store.js +1 -1
  175. package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
  176. package/dist/src/agent/memory/dreaming/events.js +1 -1
  177. package/dist/src/agent/memory/dreaming/last-run.js +1 -1
  178. package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
  179. package/dist/src/agent/memory/dreaming/preview.js +1 -1
  180. package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
  181. package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
  182. package/dist/src/agent/memory/dreaming/utils.js +1 -1
  183. package/dist/src/agent/memory/index.d.ts +2 -0
  184. package/dist/src/agent/memory/index.js +2 -1
  185. package/dist/src/agent/memory/memory-flush.d.ts +27 -0
  186. package/dist/src/agent/memory/memory-flush.js +124 -0
  187. package/dist/src/agent/memory/memory-flush.js.map +1 -0
  188. package/dist/src/agent/memory/plugin-discovery.js +1 -1
  189. package/dist/src/agent/messaging/outbound-coordinator.js +2 -1
  190. package/dist/src/agent/messaging/outbound-coordinator.js.map +1 -1
  191. package/dist/src/agent/messaging/stream-manager.d.ts +2 -0
  192. package/dist/src/agent/messaging/stream-manager.js +3 -0
  193. package/dist/src/agent/messaging/stream-manager.js.map +1 -1
  194. package/dist/src/agent/models/manager.js +1 -1
  195. package/dist/src/agent/orchestration/agent-orchestrator.d.ts +0 -4
  196. package/dist/src/agent/orchestration/agent-orchestrator.js +40 -107
  197. package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
  198. package/dist/src/agent/prompt/contribution.d.ts +9 -0
  199. package/dist/src/agent/prompt/contribution.js +1 -0
  200. package/dist/src/agent/prompt/safety.js +0 -10
  201. package/dist/src/agent/prompt/safety.js.map +1 -1
  202. package/dist/src/agent/prompt/sanitize-for-prompt.d.ts +6 -0
  203. package/dist/src/agent/prompt/sanitize-for-prompt.js +13 -0
  204. package/dist/src/agent/prompt/sanitize-for-prompt.js.map +1 -0
  205. package/dist/src/agent/prompt/sections/behavior.d.ts +5 -0
  206. package/dist/src/agent/prompt/sections/behavior.js +50 -0
  207. package/dist/src/agent/prompt/sections/behavior.js.map +1 -0
  208. package/dist/src/agent/prompt/sections/memory-skills.d.ts +9 -0
  209. package/dist/src/agent/prompt/sections/memory-skills.js +59 -0
  210. package/dist/src/agent/prompt/sections/memory-skills.js.map +1 -0
  211. package/dist/src/agent/prompt/sections/messaging-runtime.d.ts +16 -0
  212. package/dist/src/agent/prompt/sections/messaging-runtime.js +69 -0
  213. package/dist/src/agent/prompt/sections/messaging-runtime.js.map +1 -0
  214. package/dist/src/agent/prompt/sections/project-context.d.ts +16 -0
  215. package/dist/src/agent/prompt/sections/project-context.js +58 -0
  216. package/dist/src/agent/prompt/sections/project-context.js.map +1 -0
  217. package/dist/src/agent/prompt/sections/tooling.d.ts +5 -0
  218. package/dist/src/agent/prompt/sections/tooling.js +119 -0
  219. package/dist/src/agent/prompt/sections/tooling.js.map +1 -0
  220. package/dist/src/agent/prompt/sections/workspace-runtime.d.ts +11 -0
  221. package/dist/src/agent/prompt/sections/workspace-runtime.js +32 -0
  222. package/dist/src/agent/prompt/sections/workspace-runtime.js.map +1 -0
  223. package/dist/src/agent/prompt/service-prompt-builder.d.ts +13 -0
  224. package/dist/src/agent/prompt/service-prompt-builder.js +56 -15
  225. package/dist/src/agent/prompt/service-prompt-builder.js.map +1 -1
  226. package/dist/src/agent/prompt/subagent-context.d.ts +15 -0
  227. package/dist/src/agent/prompt/subagent-context.js +68 -0
  228. package/dist/src/agent/prompt/subagent-context.js.map +1 -0
  229. package/dist/src/agent/prompt/system-prompt-params.d.ts +53 -0
  230. package/dist/src/agent/prompt/system-prompt-params.js +74 -0
  231. package/dist/src/agent/prompt/system-prompt-params.js.map +1 -0
  232. package/dist/src/agent/prompt/system-prompt.d.ts +22 -11
  233. package/dist/src/agent/prompt/system-prompt.js +79 -193
  234. package/dist/src/agent/prompt/system-prompt.js.map +1 -1
  235. package/dist/src/agent/prompt/types.d.ts +3 -0
  236. package/dist/src/agent/reply/post-compaction-context.js +1 -1
  237. package/dist/src/agent/reply/workspace-boundary-read.js +1 -1
  238. package/dist/src/agent/sandbox/path-policy.js +2 -2
  239. package/dist/src/agent/service/build-direct-message-content.d.ts +5 -21
  240. package/dist/src/agent/service/build-direct-message-content.js +11 -64
  241. package/dist/src/agent/service/build-direct-message-content.js.map +1 -1
  242. package/dist/src/agent/service/direct-turn-helpers.d.ts +1 -1
  243. package/dist/src/agent/service/direct-turn-helpers.js +7 -0
  244. package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
  245. package/dist/src/agent/service/process-direct-one-shot.d.ts +3 -3
  246. package/dist/src/agent/service/process-direct-one-shot.js +22 -17
  247. package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
  248. package/dist/src/agent/service/process-direct-streaming.d.ts +6 -20
  249. package/dist/src/agent/service/process-direct-streaming.js +39 -40
  250. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  251. package/dist/src/agent/service/webchat-tts.d.ts +4 -9
  252. package/dist/src/agent/service/webchat-tts.js +9 -20
  253. package/dist/src/agent/service/webchat-tts.js.map +1 -1
  254. package/dist/src/agent/service.d.ts +8 -19
  255. package/dist/src/agent/service.js +57 -15
  256. package/dist/src/agent/service.js.map +1 -1
  257. package/dist/src/agent/session/session-inspector.js +1 -1
  258. package/dist/src/agent/skills/config.js +1 -1
  259. package/dist/src/agent/skills/hub-hash.js +2 -2
  260. package/dist/src/agent/skills/hub-lock.js +1 -1
  261. package/dist/src/agent/skills/hub-pull.js +2 -2
  262. package/dist/src/agent/skills/index.js +1 -1
  263. package/dist/src/agent/skills/managed-store.js +1 -1
  264. package/dist/src/agent/skills/scanner.js +1 -1
  265. package/dist/src/agent/skills/skill-manage-ops.js +1 -1
  266. package/dist/src/agent/skills/skill-manager.js +1 -1
  267. package/dist/src/agent/tools/dreaming-tool.js +1 -1
  268. package/dist/src/agent/tools/factory.js +3 -1
  269. package/dist/src/agent/tools/factory.js.map +1 -1
  270. package/dist/src/agent/tools/image-generate-tool.js +1 -1
  271. package/dist/src/agent/tools/index.d.ts +1 -0
  272. package/dist/src/agent/tools/index.js +2 -1
  273. package/dist/src/agent/tools/media-read-tool.d.ts +2 -0
  274. package/dist/src/agent/tools/media-read-tool.js +89 -0
  275. package/dist/src/agent/tools/media-read-tool.js.map +1 -0
  276. package/dist/src/agent/tools/send-media.js +1 -1
  277. package/dist/src/agent/tools/skill-manage-tool.js +1 -1
  278. package/dist/src/agent/tools/workflow-tool.js +1 -1
  279. package/dist/src/agent/tools/write.js +1 -1
  280. package/dist/src/agent/workflow/catalog.js +1 -1
  281. package/dist/src/auth/credentials.js +3 -3
  282. package/dist/src/auth/profiles/store.js +1 -1
  283. package/dist/src/auth/sync-provider-auth.js +1 -1
  284. package/dist/src/browser/cache-dir-policy.js +1 -1
  285. package/dist/src/browser/cdp-local-launcher.js +2 -2
  286. package/dist/src/browser/providers/browser-ext-install.js +3 -3
  287. package/dist/src/browser/providers/cloakbrowser.js +4 -4
  288. package/dist/src/browser/providers/playwright-doctor.js +1 -1
  289. package/dist/src/browser/stealth.js +1 -1
  290. package/dist/src/channels/attachments/attachment-classifiers.d.ts +8 -0
  291. package/dist/src/channels/attachments/attachment-classifiers.js +11 -0
  292. package/dist/src/channels/attachments/attachment-classifiers.js.map +1 -0
  293. package/dist/src/channels/attachments/inbound-persist.d.ts +13 -34
  294. package/dist/src/channels/attachments/inbound-persist.js +34 -102
  295. package/dist/src/channels/attachments/inbound-persist.js.map +1 -1
  296. package/dist/src/channels/attachments/inbound-types.d.ts +13 -0
  297. package/dist/src/channels/attachments/inbound-types.js +30 -0
  298. package/dist/src/channels/attachments/inbound-types.js.map +1 -0
  299. package/dist/src/channels/attachments/outbound-tts-persist.d.ts +3 -12
  300. package/dist/src/channels/attachments/outbound-tts-persist.js +28 -48
  301. package/dist/src/channels/attachments/outbound-tts-persist.js.map +1 -1
  302. package/dist/src/channels/attachments/voice-stt-webchat.d.ts +3 -3
  303. package/dist/src/channels/attachments/voice-stt-webchat.js +8 -14
  304. package/dist/src/channels/attachments/voice-stt-webchat.js.map +1 -1
  305. package/dist/src/channels/channel-domain.d.ts +30 -2
  306. package/dist/src/channels/exec-approval-runtime.d.ts +29 -0
  307. package/dist/src/channels/exec-approval-runtime.js +30 -0
  308. package/dist/src/channels/exec-approval-runtime.js.map +1 -0
  309. package/dist/src/channels/index.js +1 -1
  310. package/dist/src/channels/outbound/persist-store.js +1 -1
  311. package/dist/src/channels/pairing/allow-from-file.js +1 -1
  312. package/dist/src/channels/pairing/pairing-store.js +2 -2
  313. package/dist/src/channels/plugin-types.d.ts +1 -0
  314. package/dist/src/channels/transport-types.d.ts +2 -2
  315. package/dist/src/chat-commands/agent-edit.js +2 -2
  316. package/dist/src/chat-commands/builtins/config.js +2 -2
  317. package/dist/src/chat-commands/context.js +1 -1
  318. package/dist/src/cli/commands/channels.js +1 -1
  319. package/dist/src/cli/commands/config.js +1 -1
  320. package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
  321. package/dist/src/cli/commands/doctor/checks/database-schema.d.ts +2 -0
  322. package/dist/src/cli/commands/doctor/checks/database-schema.js +62 -0
  323. package/dist/src/cli/commands/doctor/checks/database-schema.js.map +1 -0
  324. package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
  325. package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
  326. package/dist/src/cli/commands/doctor/checks/workspace-status.js +1 -1
  327. package/dist/src/cli/commands/doctor/flow.js +2 -0
  328. package/dist/src/cli/commands/doctor/flow.js.map +1 -1
  329. package/dist/src/cli/commands/extension-dev.js +1 -1
  330. package/dist/src/cli/commands/extension-marketplace.js +1 -1
  331. package/dist/src/cli/commands/extension-pack.js +1 -1
  332. package/dist/src/cli/commands/gateway/logs.js +1 -1
  333. package/dist/src/cli/commands/image.js +1 -1
  334. package/dist/src/cli/commands/init.js +5 -60
  335. package/dist/src/cli/commands/init.js.map +1 -1
  336. package/dist/src/cli/commands/onboard/channels/registry.js +1 -1
  337. package/dist/src/cli/commands/setup.js +6 -5
  338. package/dist/src/cli/commands/setup.js.map +1 -1
  339. package/dist/src/cli/utils/init-workspace-core.js +2 -3
  340. package/dist/src/cli/utils/init-workspace-core.js.map +1 -1
  341. package/dist/src/commands/agents.config.js +1 -1
  342. package/dist/src/config/agent-profile.js +1 -1
  343. package/dist/src/config/gateway-bind.js +1 -1
  344. package/dist/src/config/index.js +7 -7
  345. package/dist/src/config/loader.js +2 -2
  346. package/dist/src/config/models-json.js +2 -2
  347. package/dist/src/config/paths-state.js +1 -1
  348. package/dist/src/config/paths.d.ts +0 -13
  349. package/dist/src/config/paths.js +1 -20
  350. package/dist/src/config/paths.js.map +1 -1
  351. package/dist/src/config/profile.js +2 -2
  352. package/dist/src/config/rules.js +1 -1
  353. package/dist/src/config/schema.d.ts +24 -0
  354. package/dist/src/config/schema.js +33 -2
  355. package/dist/src/config/schema.js.map +1 -1
  356. package/dist/src/config/workspace-path.js +1 -1
  357. package/dist/src/cron/executor.js +4 -4
  358. package/dist/src/cron/persistence.js +1 -1
  359. package/dist/src/daemon/constants.js +1 -1
  360. package/dist/src/daemon/install-plan.js +2 -2
  361. package/dist/src/daemon/launchd.js +2 -2
  362. package/dist/src/daemon/schtasks.js +2 -2
  363. package/dist/src/daemon/systemd.js +2 -2
  364. package/dist/src/extensions/bundle-mcp.js +1 -1
  365. package/dist/src/extensions/discover-extensions.js +1 -1
  366. package/dist/src/extensions/health.js +1 -1
  367. package/dist/src/extensions/loader.js +1 -1
  368. package/dist/src/extensions/lockfile.js +2 -2
  369. package/dist/src/extensions/update.js +1 -1
  370. package/dist/src/gateway/agents-admin.js +3 -3
  371. package/dist/src/gateway/file-path-classifier.js +2 -2
  372. package/dist/src/gateway/heartbeat/service.js +1 -1
  373. package/dist/src/gateway/hono/lib/config-payload.js +2 -2
  374. package/dist/src/gateway/hono/lib/extension-store.js +2 -2
  375. package/dist/src/gateway/hono/lib/static-ui.js +2 -2
  376. package/dist/src/gateway/hono/oauth.js +1 -1
  377. package/dist/src/gateway/hono/routes/agents.js +1 -1
  378. package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
  379. package/dist/src/gateway/hono/routes/config-patch/channels.js +3 -3
  380. package/dist/src/gateway/hono/routes/config-patch/channels.js.map +1 -1
  381. package/dist/src/gateway/hono/routes/config-patch/misc.js +1 -1
  382. package/dist/src/gateway/hono/routes/dreaming.js +1 -1
  383. package/dist/src/gateway/hono/routes/host-fs.js +2 -2
  384. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  385. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  386. package/dist/src/gateway/hono/routes/media.d.ts +3 -0
  387. package/dist/src/gateway/hono/routes/media.js +50 -0
  388. package/dist/src/gateway/hono/routes/media.js.map +1 -0
  389. package/dist/src/gateway/hono/routes/models.js +1 -1
  390. package/dist/src/gateway/hono/routes/sessions.js +6 -0
  391. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  392. package/dist/src/gateway/hono/routes/shares.js +1 -1
  393. package/dist/src/gateway/hono/routes/workspace.js +3 -89
  394. package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
  395. package/dist/src/gateway/lock.js +3 -3
  396. package/dist/src/gateway/ports.js +1 -1
  397. package/dist/src/gateway/service/agent-runner.js +2 -2
  398. package/dist/src/gateway/service/marketplace-service.js +2 -2
  399. package/dist/src/gateway/service/sessions-api.js +3 -0
  400. package/dist/src/gateway/service/sessions-api.js.map +1 -1
  401. package/dist/src/gateway/service.js +1 -1
  402. package/dist/src/gateway/workspace-fs-file-list.js +1 -1
  403. package/dist/src/heartbeat/index.js +1 -1
  404. package/dist/src/infra/brew.js +1 -1
  405. package/dist/src/infra/package-json.js +1 -1
  406. package/dist/src/infra/package-update-steps.js +1 -1
  407. package/dist/src/infra/path-env.js +2 -2
  408. package/dist/src/infra/restart.js +2 -2
  409. package/dist/src/infra/stable-node-path.js +1 -1
  410. package/dist/src/infra/update-check.js +1 -1
  411. package/dist/src/infra/update-global.js +1 -1
  412. package/dist/src/infra/update-lock.js +3 -3
  413. package/dist/src/infra/update-runner.js +1 -1
  414. package/dist/src/infra/update-startup.js +2 -2
  415. package/dist/src/infra/write-file-atomic.js +2 -2
  416. package/dist/src/media/index.d.ts +5 -0
  417. package/dist/src/media/index.js +5 -0
  418. package/dist/src/media/media-reference.d.ts +23 -0
  419. package/dist/src/media/media-reference.js +34 -0
  420. package/dist/src/media/media-reference.js.map +1 -0
  421. package/dist/src/media/paths.d.ts +5 -0
  422. package/dist/src/media/paths.js +20 -0
  423. package/dist/src/media/paths.js.map +1 -0
  424. package/dist/src/media/session-references.d.ts +8 -0
  425. package/dist/src/media/session-references.js +45 -0
  426. package/dist/src/media/session-references.js.map +1 -0
  427. package/dist/src/media/store.d.ts +19 -0
  428. package/dist/src/media/store.js +131 -0
  429. package/dist/src/media/store.js.map +1 -0
  430. package/dist/src/media/types.d.ts +34 -0
  431. package/dist/src/media/types.js +1 -0
  432. package/dist/src/media/uri.d.ts +15 -0
  433. package/dist/src/media/uri.js +51 -0
  434. package/dist/src/media/uri.js.map +1 -0
  435. package/dist/src/notes/store.js +2 -2
  436. package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
  437. package/dist/src/providers/index.js +2 -2
  438. package/dist/src/providers/model-registry.js +1 -1
  439. package/dist/src/session/init-session-turn.js +2 -2
  440. package/dist/src/session/resolve-session.js +3 -3
  441. package/dist/src/session/session-title.js +8 -5
  442. package/dist/src/session/session-title.js.map +1 -1
  443. package/dist/src/session/store.js +3 -3
  444. package/dist/src/session/store.js.map +1 -1
  445. package/dist/src/session/types.d.ts +12 -7
  446. package/dist/src/session/types.js.map +1 -1
  447. package/dist/src/share/share-auto.js +2 -2
  448. package/dist/src/share/share-store.js +3 -3
  449. package/dist/src/share/share-thumbnail.js +2 -2
  450. package/dist/src/share/share-zip.js +1 -1
  451. package/dist/src/share/site-share-store.js +3 -3
  452. package/dist/src/share/site-static-serve.js +1 -1
  453. package/dist/src/storage/sqlite/connection.js +2 -2
  454. package/dist/src/storage/sqlite/index.d.ts +4 -1
  455. package/dist/src/storage/sqlite/index.js +6 -2
  456. package/dist/src/storage/sqlite/memory-index-repository.js +2 -2
  457. package/dist/src/storage/sqlite/migrations/002_add_flush_columns.sql +3 -0
  458. package/dist/src/storage/sqlite/migrations/discover.d.ts +4 -0
  459. package/dist/src/storage/sqlite/migrations/discover.js +38 -0
  460. package/dist/src/storage/sqlite/migrations/discover.js.map +1 -0
  461. package/dist/src/storage/sqlite/migrations/discover.ts +52 -0
  462. package/dist/src/storage/sqlite/migrations/errors.d.ts +11 -0
  463. package/dist/src/storage/sqlite/migrations/errors.js +27 -0
  464. package/dist/src/storage/sqlite/migrations/errors.js.map +1 -0
  465. package/dist/src/storage/sqlite/migrations/errors.ts +32 -0
  466. package/dist/src/storage/sqlite/migrations/runner.d.ts +22 -0
  467. package/dist/src/storage/sqlite/migrations/runner.js +97 -0
  468. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -0
  469. package/dist/src/storage/sqlite/migrations/runner.ts +153 -0
  470. package/dist/src/storage/sqlite/migrations/types.d.ts +12 -0
  471. package/dist/src/storage/sqlite/migrations/types.js +1 -0
  472. package/dist/src/storage/sqlite/migrations/types.ts +13 -0
  473. package/dist/src/storage/sqlite/row-mappers.d.ts +4 -0
  474. package/dist/src/storage/sqlite/row-mappers.js +4 -0
  475. package/dist/src/storage/sqlite/row-mappers.js.map +1 -1
  476. package/dist/src/storage/sqlite/schema-version.d.ts +6 -0
  477. package/dist/src/storage/sqlite/schema-version.js +29 -0
  478. package/dist/src/storage/sqlite/schema-version.js.map +1 -0
  479. package/dist/src/storage/sqlite/schema.d.ts +5 -3
  480. package/dist/src/storage/sqlite/schema.js +25 -29
  481. package/dist/src/storage/sqlite/schema.js.map +1 -1
  482. package/dist/src/storage/sqlite/schema.sql +2 -0
  483. package/dist/src/storage/sqlite/session-metadata.js +1 -0
  484. package/dist/src/storage/sqlite/session-metadata.js.map +1 -1
  485. package/dist/src/storage/sqlite/session-repository.js +7 -3
  486. package/dist/src/storage/sqlite/session-repository.js.map +1 -1
  487. package/dist/src/tui/clipboard-image.js +3 -3
  488. package/dist/src/tui/theme-manager.js +1 -1
  489. package/dist/src/tui/tui-keybindings-file.js +1 -1
  490. package/dist/src/tui/tui-scoped-models.js +2 -2
  491. package/dist/src/tui/tui-settings.js +1 -1
  492. package/dist/src/tui/tui.js +3 -3
  493. package/dist/src/tunnel/frpc-binary.js +3 -3
  494. package/dist/src/tunnel/frpc-config.js +1 -1
  495. package/dist/src/tunnel/frpc-extract.js +1 -1
  496. package/dist/src/tunnel/tunnel-state.js +1 -1
  497. package/dist/src/utils/logger/audit.js +1 -1
  498. package/dist/src/utils/logger/log-store.js +1 -1
  499. package/dist/src/utils/logger/rotation.js +1 -1
  500. package/dist/src/voice/tts/audio.js +1 -1
  501. package/dist/src/voice/tts/providers/edge-speech.js +2 -2
  502. package/dist/src/workflows/store/event-store.js +1 -1
  503. package/dist/src/workflows/store/run-store.js +1 -1
  504. package/package.json +1 -1
  505. package/dist/gateway/static/root/assets/channels-settings-BmN5udFV.js +0 -1
  506. package/dist/gateway/static/root/assets/channels-status-swr-LXGc1lXo.js +0 -8
  507. package/dist/gateway/static/root/assets/index-BJDmBCSl.css +0 -1
  508. package/dist/gateway/static/root/assets/index-DsmZjtSc.js +0 -4707
  509. package/dist/gateway/static/root/assets/url-DpFBIyN9.js +0 -3
  510. package/dist/src/cli/commands/onboard/workspace.d.ts +0 -14
  511. package/dist/src/cli/commands/onboard/workspace.js +0 -33
  512. package/dist/src/cli/commands/onboard/workspace.js.map +0 -1
  513. package/dist/src/cli/utils/workspace.d.ts +0 -35
  514. package/dist/src/cli/utils/workspace.js +0 -72
  515. package/dist/src/cli/utils/workspace.js.map +0 -1
@@ -1,39 +1,18 @@
1
1
  /**
2
- * Persist inbound channel / Web UI uploads under the agent home dir so the markdown
3
- * workspace stays user-visible; session transcripts reference stable relative paths.
2
+ * Persist inbound uploads to the global media store (`{stateDir}/media/inbound/`).
4
3
  */
5
- /** New layout: `<agentHome>/inbound/<session>/` rel paths use this prefix. */
6
- export declare const INBOUND_REL_ROOT = "inbound";
7
- export interface InboundAttachmentInput {
8
- type: string;
9
- mimeType?: string;
10
- data?: string;
11
- name?: string;
12
- size?: number;
13
- /** Set after persist (relative to agent home). */
14
- workspaceRelativePath?: string;
15
- }
16
- export type InternalAttachmentRoots = {
17
- /** `…/agents/<id>/` — primary storage */
18
- agentHome: string;
19
- };
20
- /** Decode base64 or data-URL payload for inbound attachments (also used by voice STT). */
4
+ import type { InboundAttachmentInput, MediaRef } from '../../media/types.js';
5
+ export type { InboundAttachmentInput, MediaRef } from '../../media/types.js';
6
+ export { isImageInboundAttachment, isVoiceInboundAttachment } from './attachment-classifiers.js';
7
+ export { toMediaRef, mediaRefFromUri } from './inbound-types.js';
8
+ /** Decode base64 or data-URL payload (composer wire + voice STT). */
21
9
  export declare function decodeInboundAttachmentBase64(data: string): Buffer;
22
10
  /**
23
- * Write non-image attachments with binary data to disk; returns a shallow copy
24
- * of each attachment with `workspaceRelativePath` set (POSIX-style, `/` separators).
11
+ * Write attachments with binary `data` to the media store; returns metadata-only refs.
25
12
  */
26
- export declare function persistInboundAttachmentsToWorkspace(agentHomeRoot: string, sessionKey: string, attachments: InboundAttachmentInput[] | undefined): Promise<InboundAttachmentInput[] | undefined>;
27
- /**
28
- * Build transcript text for a non-image file for the LLM (includes machine-readable path lines).
29
- */
30
- export declare function formatInboundFileTextBlock(att: InboundAttachmentInput, storageRootAbs: string): string;
31
- /**
32
- * Remove inbound file transcript blocks from a string (e.g. auto session titles).
33
- * Matches Web UI `stripInboundFileMachineText`, plus bare `[File: …]` lines when paths are absent.
34
- */
35
- export declare function stripInboundFileMetadataFromText(text: string): string;
36
- /**
37
- * Resolve a stored relative path under `inbound/`.
38
- */
39
- export declare function resolveSafeInboundFilePath(roots: InternalAttachmentRoots, relRaw: string): string | null;
13
+ export declare function persistInboundAttachments(attachments: InboundAttachmentInput[] | undefined, opts?: {
14
+ maxBytes?: number;
15
+ }): Promise<MediaRef[] | undefined>;
16
+ /** Read attachment bytes by media URI (voice STT, tools). */
17
+ export declare function readInboundAttachmentBuffer(uri: string): Promise<Buffer>;
18
+ export declare function assertMediaUri(uri: string): void;
@@ -1,125 +1,57 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
- import { mkdir, writeFile } from "fs/promises";
4
- import { join, resolve } from "path";
5
- import { randomBytes } from "crypto";
3
+ import { parseMediaUri } from "../../media/uri.js";
4
+ import { saveMediaBuffer } from "../../media/store.js";
5
+ import { readMediaReference, resolveMediaReference } from "../../media/media-reference.js";
6
+ import { isImageInboundAttachment, isVoiceInboundAttachment } from "./attachment-classifiers.js";
7
+ import { mediaRefFromUri, toMediaRef } from "./inbound-types.js";
6
8
  //#region src/channels/attachments/inbound-persist.ts
7
- /**
8
- * Persist inbound channel / Web UI uploads under the agent home dir so the markdown
9
- * workspace stays user-visible; session transcripts reference stable relative paths.
10
- */
11
9
  init_logger();
12
10
  const log = createLogger("InboundPersist");
13
- /** New layout: `<agentHome>/inbound/<session>/` rel paths use this prefix. */
14
- const INBOUND_REL_ROOT = "inbound";
15
- function sanitizeSessionSegment(sessionKey) {
16
- return sessionKey.replace(/[^a-zA-Z0-9_.-]+/g, "_").slice(0, 180) || "session";
17
- }
18
- function sanitizeFilename(name) {
19
- return (name.replace(/[/\\?%*:|"<>]/g, "_").trim() || "file").slice(0, 200);
20
- }
21
- function isImageAttachment(att) {
22
- return att.type === "image" || att.type === "photo" || att.mimeType?.startsWith("image/") === true;
23
- }
24
- /** Decode base64 or data-URL payload for inbound attachments (also used by voice STT). */
11
+ /** Decode base64 or data-URL payload (composer wire + voice STT). */
25
12
  function decodeInboundAttachmentBase64(data) {
26
13
  const trimmed = data.trim();
27
14
  const b64 = trimmed.startsWith("data:") ? trimmed.split(/base64,/)[1] ?? trimmed : trimmed;
28
15
  return Buffer.from(b64.replace(/\s/g, ""), "base64");
29
16
  }
30
- function resolveUnderRoot(root, rel, requiredPrefix) {
31
- const normalized = rel.replace(/\\/g, "/").replace(/^\/+/, "");
32
- if (normalized.includes("..") || !normalized.startsWith(requiredPrefix)) return null;
33
- const abs = resolve(root, ...normalized.split("/"));
34
- const rootResolved = resolve(root);
35
- if (!abs.startsWith(rootResolved)) return null;
36
- return abs;
37
- }
38
17
  /**
39
- * Write non-image attachments with binary data to disk; returns a shallow copy
40
- * of each attachment with `workspaceRelativePath` set (POSIX-style, `/` separators).
18
+ * Write attachments with binary `data` to the media store; returns metadata-only refs.
41
19
  */
42
- async function persistInboundAttachmentsToWorkspace(agentHomeRoot, sessionKey, attachments) {
43
- if (!attachments?.length) return attachments;
44
- const sessionSeg = sanitizeSessionSegment(sessionKey);
45
- const inboundAbs = resolve(agentHomeRoot, INBOUND_REL_ROOT, sessionSeg);
46
- await mkdir(inboundAbs, { recursive: true });
20
+ async function persistInboundAttachments(attachments, opts) {
21
+ if (!attachments?.length) return void 0;
22
+ const maxBytes = opts?.maxBytes ?? 5242880;
47
23
  const out = [];
48
24
  for (const att of attachments) {
49
- if (att.workspaceRelativePath) {
50
- out.push({ ...att });
51
- continue;
52
- }
53
- if (isImageAttachment(att)) {
54
- out.push({ ...att });
55
- continue;
56
- }
57
- if (!att.data || att.data.length === 0) {
58
- out.push({ ...att });
25
+ if (att.uri?.trim()) {
26
+ const resolved = await resolveMediaReference(att.uri.trim());
27
+ out.push(mediaRefFromUri(att, resolved.uri, resolved.path, resolved.bucket, resolved.id));
59
28
  continue;
60
29
  }
61
- try {
62
- const buf = decodeInboundAttachmentBase64(att.data);
63
- const fname = `${randomBytes(8).toString("hex")}_${sanitizeFilename(att.name || "file")}`;
64
- await writeFile(join(inboundAbs, fname), buf);
65
- const workspaceRelativePath = [
66
- INBOUND_REL_ROOT,
67
- sessionSeg,
68
- fname
69
- ].join("/");
70
- log.debug({
71
- sessionKey,
72
- workspaceRelativePath,
73
- bytes: buf.length
74
- }, "Inbound file persisted");
75
- out.push({
76
- ...att,
77
- workspaceRelativePath,
78
- size: att.size ?? buf.length
79
- });
80
- } catch (err) {
81
- log.warn({
82
- err,
83
- sessionKey,
84
- name: att.name
85
- }, "Failed to persist inbound attachment");
86
- out.push({ ...att });
87
- }
30
+ if (!att.data || att.data.length === 0) throw new Error(`Inbound attachment "${att.name ?? "file"}" is missing data and uri`);
31
+ const saved = await saveMediaBuffer(decodeInboundAttachmentBase64(att.data), {
32
+ contentType: att.mimeType,
33
+ bucket: "inbound",
34
+ maxBytes,
35
+ originalFilename: att.name
36
+ });
37
+ log.debug({
38
+ uri: saved.uri,
39
+ bytes: saved.size,
40
+ name: att.name
41
+ }, "Inbound attachment persisted");
42
+ out.push(toMediaRef(att, saved));
88
43
  }
89
- return out;
44
+ return out.length ? out : void 0;
90
45
  }
91
- /**
92
- * Build transcript text for a non-image file for the LLM (includes machine-readable path lines).
93
- */
94
- function formatInboundFileTextBlock(att, storageRootAbs) {
95
- const head = `[File: ${att.name || "unknown"} (${att.mimeType || "unknown type"}, ${att.size ?? 0} bytes)]`;
96
- if (!att.workspaceRelativePath) return head;
97
- const rel = att.workspaceRelativePath.replace(/\\/g, "/");
98
- return `${head}\nxopc-path:rel:${rel}\nxopc-path:abs:${resolve(storageRootAbs, ...rel.split("/").filter(Boolean))}`;
46
+ /** Read attachment bytes by media URI (voice STT, tools). */
47
+ async function readInboundAttachmentBuffer(uri) {
48
+ const { buffer } = await readMediaReference(uri);
49
+ return buffer;
99
50
  }
100
- /**
101
- * Remove inbound file transcript blocks from a string (e.g. auto session titles).
102
- * Matches Web UI `stripInboundFileMachineText`, plus bare `[File: …]` lines when paths are absent.
103
- */
104
- function stripInboundFileMetadataFromText(text) {
105
- if (!text.includes("[File:") && !text.includes("xopc-path:")) return text;
106
- let out = text;
107
- out = out.replace(/\s*\[File:[^\]]+\]\s*\r?\nxopc-path:rel:[^\r\n]+\r?\n\s*xopc-path:abs:[^\r\n]+/g, "");
108
- out = out.replace(/\s*\[File:[^\]]+\]\s+xopc-path:rel:\S+\s+xopc-path:abs:\S+/g, "");
109
- out = out.replace(/\s*\[File:[^\]]+\]\s*xopc-path:rel:\S+\s*xopc-path:abs:\S+/g, "");
110
- out = out.replace(/\s*\[File:[^\]]+\]\s*/g, " ");
111
- return out.replace(/\n{3,}/g, "\n\n").replace(/\s{2,}/g, " ").trim();
112
- }
113
- /**
114
- * Resolve a stored relative path under `inbound/`.
115
- */
116
- function resolveSafeInboundFilePath(roots, relRaw) {
117
- const rel = relRaw.replace(/\\/g, "/").replace(/^\/+/, "");
118
- if (rel.includes("..")) return null;
119
- if (rel.startsWith(`inbound/`)) return resolveUnderRoot(roots.agentHome, rel, `${INBOUND_REL_ROOT}/`);
120
- return null;
51
+ function assertMediaUri(uri) {
52
+ parseMediaUri(uri);
121
53
  }
122
54
  //#endregion
123
- export { INBOUND_REL_ROOT, decodeInboundAttachmentBase64, formatInboundFileTextBlock, persistInboundAttachmentsToWorkspace, resolveSafeInboundFilePath, stripInboundFileMetadataFromText };
55
+ export { assertMediaUri, decodeInboundAttachmentBase64, isImageInboundAttachment, isVoiceInboundAttachment, mediaRefFromUri, persistInboundAttachments, readInboundAttachmentBuffer, toMediaRef };
124
56
 
125
57
  //# sourceMappingURL=inbound-persist.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"inbound-persist.js","names":[],"sources":["../../../../src/channels/attachments/inbound-persist.ts"],"sourcesContent":["/**\n * Persist inbound channel / Web UI uploads under the agent home dir so the markdown\n * workspace stays user-visible; session transcripts reference stable relative paths.\n */\n\nimport { mkdir, writeFile } from 'fs/promises';\nimport { join, resolve } from 'path';\nimport { randomBytes } from 'crypto';\nimport { createLogger } from '../../utils/logger.js';\nconst log = createLogger('InboundPersist');\n\n/** New layout: `<agentHome>/inbound/<session>/` — rel paths use this prefix. */\nexport const INBOUND_REL_ROOT = 'inbound';\n\nexport interface InboundAttachmentInput {\n type: string;\n mimeType?: string;\n data?: string;\n name?: string;\n size?: number;\n /** Set after persist (relative to agent home). */\n workspaceRelativePath?: string;\n}\n\nexport type InternalAttachmentRoots = {\n /** `…/agents/<id>/` primary storage */\n agentHome: string;\n};\n\nfunction sanitizeSessionSegment(sessionKey: string): string {\n return sessionKey.replace(/[^a-zA-Z0-9_.-]+/g, '_').slice(0, 180) || 'session';\n}\n\nfunction sanitizeFilename(name: string): string {\n const base = name.replace(/[/\\\\?%*:|\"<>]/g, '_').trim() || 'file';\n return base.slice(0, 200);\n}\n\nfunction isImageAttachment(att: InboundAttachmentInput): boolean {\n return att.type === 'image' || att.type === 'photo' || att.mimeType?.startsWith('image/') === true;\n}\n\n/** Decode base64 or data-URL payload for inbound attachments (also used by voice STT). */\nexport function decodeInboundAttachmentBase64(data: string): Buffer {\n const trimmed = data.trim();\n const b64 = trimmed.startsWith('data:') ? (trimmed.split(/base64,/)[1] ?? trimmed) : trimmed;\n return Buffer.from(b64.replace(/\\s/g, ''), 'base64');\n}\n\nfunction resolveUnderRoot(root: string, rel: string, requiredPrefix: string): string | null {\n const normalized = rel.replace(/\\\\/g, '/').replace(/^\\/+/, '');\n if (normalized.includes('..') || !normalized.startsWith(requiredPrefix)) {\n return null;\n }\n const abs = resolve(root, ...normalized.split('/'));\n const rootResolved = resolve(root);\n if (!abs.startsWith(rootResolved)) {\n return null;\n }\n return abs;\n}\n\n/**\n * Write non-image attachments with binary data to disk; returns a shallow copy\n * of each attachment with `workspaceRelativePath` set (POSIX-style, `/` separators).\n */\nexport async function persistInboundAttachmentsToWorkspace(\n agentHomeRoot: string,\n sessionKey: string,\n attachments: InboundAttachmentInput[] | undefined,\n): Promise<InboundAttachmentInput[] | undefined> {\n if (!attachments?.length) return attachments;\n\n const sessionSeg = sanitizeSessionSegment(sessionKey);\n const inboundAbs = resolve(agentHomeRoot, INBOUND_REL_ROOT, sessionSeg);\n await mkdir(inboundAbs, { recursive: true });\n\n const out: InboundAttachmentInput[] = [];\n\n for (const att of attachments) {\n if (att.workspaceRelativePath) {\n out.push({ ...att });\n continue;\n }\n if (isImageAttachment(att)) {\n out.push({ ...att });\n continue;\n }\n if (!att.data || att.data.length === 0) {\n out.push({ ...att });\n continue;\n }\n\n try {\n const buf = decodeInboundAttachmentBase64(att.data);\n const id = randomBytes(8).toString('hex');\n const fname = `${id}_${sanitizeFilename(att.name || 'file')}`;\n const absFile = join(inboundAbs, fname);\n await writeFile(absFile, buf);\n\n const workspaceRelativePath = [INBOUND_REL_ROOT, sessionSeg, fname].join('/');\n\n log.debug({ sessionKey, workspaceRelativePath, bytes: buf.length }, 'Inbound file persisted');\n\n out.push({\n ...att,\n workspaceRelativePath,\n size: att.size ?? buf.length,\n });\n } catch (err) {\n log.warn({ err, sessionKey, name: att.name }, 'Failed to persist inbound attachment');\n out.push({ ...att });\n }\n }\n\n return out;\n}\n\n/**\n * Build transcript text for a non-image file for the LLM (includes machine-readable path lines).\n */\nexport function formatInboundFileTextBlock(\n att: InboundAttachmentInput,\n storageRootAbs: string,\n): string {\n const name = att.name || 'unknown';\n const mime = att.mimeType || 'unknown type';\n const size = att.size ?? 0;\n const head = `[File: ${name} (${mime}, ${size} bytes)]`;\n if (!att.workspaceRelativePath) {\n return head;\n }\n const rel = att.workspaceRelativePath.replace(/\\\\/g, '/');\n const abs = resolve(storageRootAbs, ...rel.split('/').filter(Boolean));\n return `${head}\\nxopc-path:rel:${rel}\\nxopc-path:abs:${abs}`;\n}\n\n/**\n * Remove inbound file transcript blocks from a string (e.g. auto session titles).\n * Matches Web UI `stripInboundFileMachineText`, plus bare `[File: …]` lines when paths are absent.\n */\nexport function stripInboundFileMetadataFromText(text: string): string {\n if (!text.includes('[File:') && !text.includes('xopc-path:')) return text;\n let out = text;\n out = out.replace(\n /\\s*\\[File:[^\\]]+\\]\\s*\\r?\\nxopc-path:rel:[^\\r\\n]+\\r?\\n\\s*xopc-path:abs:[^\\r\\n]+/g,\n '',\n );\n out = out.replace(/\\s*\\[File:[^\\]]+\\]\\s+xopc-path:rel:\\S+\\s+xopc-path:abs:\\S+/g, '');\n out = out.replace(/\\s*\\[File:[^\\]]+\\]\\s*xopc-path:rel:\\S+\\s*xopc-path:abs:\\S+/g, '');\n out = out.replace(/\\s*\\[File:[^\\]]+\\]\\s*/g, ' ');\n return out.replace(/\\n{3,}/g, '\\n\\n').replace(/\\s{2,}/g, ' ').trim();\n}\n\n/**\n * Resolve a stored relative path under `inbound/`.\n */\nexport function resolveSafeInboundFilePath(\n roots: InternalAttachmentRoots,\n relRaw: string,\n): string | null {\n const rel = relRaw.replace(/\\\\/g, '/').replace(/^\\/+/, '');\n if (rel.includes('..')) {\n return null;\n }\n\n if (rel.startsWith(`${INBOUND_REL_ROOT}/`)) {\n return resolveUnderRoot(roots.agentHome, rel, `${INBOUND_REL_ROOT}/`);\n }\n\n return null;\n}\n"],"mappings":";;;;;;;;;;aAQqD;AACrD,MAAM,MAAM,aAAa,iBAAiB;;AAG1C,MAAa,mBAAmB;AAiBhC,SAAS,uBAAuB,YAA4B;AAC1D,QAAO,WAAW,QAAQ,qBAAqB,IAAI,CAAC,MAAM,GAAG,IAAI,IAAI;;AAGvE,SAAS,iBAAiB,MAAsB;AAE9C,SADa,KAAK,QAAQ,kBAAkB,IAAI,CAAC,MAAM,IAAI,QAC/C,MAAM,GAAG,IAAI;;AAG3B,SAAS,kBAAkB,KAAsC;AAC/D,QAAO,IAAI,SAAS,WAAW,IAAI,SAAS,WAAW,IAAI,UAAU,WAAW,SAAS,KAAK;;;AAIhG,SAAgB,8BAA8B,MAAsB;CAClE,MAAM,UAAU,KAAK,MAAM;CAC3B,MAAM,MAAM,QAAQ,WAAW,QAAQ,GAAI,QAAQ,MAAM,UAAU,CAAC,MAAM,UAAW;AACrF,QAAO,OAAO,KAAK,IAAI,QAAQ,OAAO,GAAG,EAAE,SAAS;;AAGtD,SAAS,iBAAiB,MAAc,KAAa,gBAAuC;CAC1F,MAAM,aAAa,IAAI,QAAQ,OAAO,IAAI,CAAC,QAAQ,QAAQ,GAAG;AAC9D,KAAI,WAAW,SAAS,KAAK,IAAI,CAAC,WAAW,WAAW,eAAe,CACrE,QAAO;CAET,MAAM,MAAM,QAAQ,MAAM,GAAG,WAAW,MAAM,IAAI,CAAC;CACnD,MAAM,eAAe,QAAQ,KAAK;AAClC,KAAI,CAAC,IAAI,WAAW,aAAa,CAC/B,QAAO;AAET,QAAO;;;;;;AAOT,eAAsB,qCACpB,eACA,YACA,aAC+C;AAC/C,KAAI,CAAC,aAAa,OAAQ,QAAO;CAEjC,MAAM,aAAa,uBAAuB,WAAW;CACrD,MAAM,aAAa,QAAQ,eAAe,kBAAkB,WAAW;AACvE,OAAM,MAAM,YAAY,EAAE,WAAW,MAAM,CAAC;CAE5C,MAAM,MAAgC,EAAE;AAExC,MAAK,MAAM,OAAO,aAAa;AAC7B,MAAI,IAAI,uBAAuB;AAC7B,OAAI,KAAK,EAAE,GAAG,KAAK,CAAC;AACpB;;AAEF,MAAI,kBAAkB,IAAI,EAAE;AAC1B,OAAI,KAAK,EAAE,GAAG,KAAK,CAAC;AACpB;;AAEF,MAAI,CAAC,IAAI,QAAQ,IAAI,KAAK,WAAW,GAAG;AACtC,OAAI,KAAK,EAAE,GAAG,KAAK,CAAC;AACpB;;AAGF,MAAI;GACF,MAAM,MAAM,8BAA8B,IAAI,KAAK;GAEnD,MAAM,QAAQ,GADH,YAAY,EAAE,CAAC,SAAS,MAChB,CAAC,GAAG,iBAAiB,IAAI,QAAQ,OAAO;AAE3D,SAAM,UADU,KAAK,YAAY,MACV,EAAE,IAAI;GAE7B,MAAM,wBAAwB;IAAC;IAAkB;IAAY;IAAM,CAAC,KAAK,IAAI;AAE7E,OAAI,MAAM;IAAE;IAAY;IAAuB,OAAO,IAAI;IAAQ,EAAE,yBAAyB;AAE7F,OAAI,KAAK;IACP,GAAG;IACH;IACA,MAAM,IAAI,QAAQ,IAAI;IACvB,CAAC;WACK,KAAK;AACZ,OAAI,KAAK;IAAE;IAAK;IAAY,MAAM,IAAI;IAAM,EAAE,uCAAuC;AACrF,OAAI,KAAK,EAAE,GAAG,KAAK,CAAC;;;AAIxB,QAAO;;;;;AAMT,SAAgB,2BACd,KACA,gBACQ;CAIR,MAAM,OAAO,UAHA,IAAI,QAAQ,UAGG,IAFf,IAAI,YAAY,eAEQ,IADxB,IAAI,QAAQ,EACqB;AAC9C,KAAI,CAAC,IAAI,sBACP,QAAO;CAET,MAAM,MAAM,IAAI,sBAAsB,QAAQ,OAAO,IAAI;AAEzD,QAAO,GAAG,KAAK,kBAAkB,IAAI,kBADzB,QAAQ,gBAAgB,GAAG,IAAI,MAAM,IAAI,CAAC,OAAO,QAAQ,CACX;;;;;;AAO5D,SAAgB,iCAAiC,MAAsB;AACrE,KAAI,CAAC,KAAK,SAAS,SAAS,IAAI,CAAC,KAAK,SAAS,aAAa,CAAE,QAAO;CACrE,IAAI,MAAM;AACV,OAAM,IAAI,QACR,mFACA,GACD;AACD,OAAM,IAAI,QAAQ,+DAA+D,GAAG;AACpF,OAAM,IAAI,QAAQ,+DAA+D,GAAG;AACpF,OAAM,IAAI,QAAQ,0BAA0B,IAAI;AAChD,QAAO,IAAI,QAAQ,WAAW,OAAO,CAAC,QAAQ,WAAW,IAAI,CAAC,MAAM;;;;;AAMtE,SAAgB,2BACd,OACA,QACe;CACf,MAAM,MAAM,OAAO,QAAQ,OAAO,IAAI,CAAC,QAAQ,QAAQ,GAAG;AAC1D,KAAI,IAAI,SAAS,KAAK,CACpB,QAAO;AAGT,KAAI,IAAI,WAAW,WAAuB,CACxC,QAAO,iBAAiB,MAAM,WAAW,KAAK,GAAG,iBAAiB,GAAG;AAGvE,QAAO"}
1
+ {"version":3,"file":"inbound-persist.js","names":[],"sources":["../../../../src/channels/attachments/inbound-persist.ts"],"sourcesContent":["/**\n * Persist inbound uploads to the global media store (`{stateDir}/media/inbound/`).\n */\n\nimport { createLogger } from '../../utils/logger.js';\nimport {\n MEDIA_MAX_BYTES,\n parseMediaUri,\n readMediaReference,\n resolveMediaReference,\n saveMediaBuffer,\n} from '../../media/index.js';\nimport type { InboundAttachmentInput, MediaRef } from '../../media/types.js';\nimport { mediaRefFromUri, toMediaRef } from './inbound-types.js';\n\nconst log = createLogger('InboundPersist');\n\nexport type { InboundAttachmentInput, MediaRef } from '../../media/types.js';\nexport { isImageInboundAttachment, isVoiceInboundAttachment } from './attachment-classifiers.js';\nexport { toMediaRef, mediaRefFromUri } from './inbound-types.js';\n\n/** Decode base64 or data-URL payload (composer wire + voice STT). */\nexport function decodeInboundAttachmentBase64(data: string): Buffer {\n const trimmed = data.trim();\n const b64 = trimmed.startsWith('data:') ? (trimmed.split(/base64,/)[1] ?? trimmed) : trimmed;\n return Buffer.from(b64.replace(/\\s/g, ''), 'base64');\n}\n\n/**\n * Write attachments with binary `data` to the media store; returns metadata-only refs.\n */\nexport async function persistInboundAttachments(\n attachments: InboundAttachmentInput[] | undefined,\n opts?: { maxBytes?: number },\n): Promise<MediaRef[] | undefined> {\n if (!attachments?.length) return undefined;\n\n const maxBytes = opts?.maxBytes ?? MEDIA_MAX_BYTES;\n const out: MediaRef[] = [];\n\n for (const att of attachments) {\n if (att.uri?.trim()) {\n const resolved = await resolveMediaReference(att.uri.trim());\n out.push(\n mediaRefFromUri(att, resolved.uri, resolved.path, resolved.bucket, resolved.id),\n );\n continue;\n }\n\n if (!att.data || att.data.length === 0) {\n throw new Error(`Inbound attachment \"${att.name ?? 'file'}\" is missing data and uri`);\n }\n\n const buf = decodeInboundAttachmentBase64(att.data);\n const saved = await saveMediaBuffer(buf, {\n contentType: att.mimeType,\n bucket: 'inbound',\n maxBytes,\n originalFilename: att.name,\n });\n\n log.debug({ uri: saved.uri, bytes: saved.size, name: att.name }, 'Inbound attachment persisted');\n\n out.push(toMediaRef(att, saved));\n }\n\n return out.length ? out : undefined;\n}\n\n/** Read attachment bytes by media URI (voice STT, tools). */\nexport async function readInboundAttachmentBuffer(uri: string): Promise<Buffer> {\n const { buffer } = await readMediaReference(uri);\n return buffer;\n}\n\nexport function assertMediaUri(uri: string): void {\n parseMediaUri(uri);\n}\n"],"mappings":";;;;;;;;aAIqD;AAWrD,MAAM,MAAM,aAAa,iBAAiB;;AAO1C,SAAgB,8BAA8B,MAAsB;CAClE,MAAM,UAAU,KAAK,MAAM;CAC3B,MAAM,MAAM,QAAQ,WAAW,QAAQ,GAAI,QAAQ,MAAM,UAAU,CAAC,MAAM,UAAW;AACrF,QAAO,OAAO,KAAK,IAAI,QAAQ,OAAO,GAAG,EAAE,SAAS;;;;;AAMtD,eAAsB,0BACpB,aACA,MACiC;AACjC,KAAI,CAAC,aAAa,OAAQ,QAAO,KAAA;CAEjC,MAAM,WAAW,MAAM,YAAA;CACvB,MAAM,MAAkB,EAAE;AAE1B,MAAK,MAAM,OAAO,aAAa;AAC7B,MAAI,IAAI,KAAK,MAAM,EAAE;GACnB,MAAM,WAAW,MAAM,sBAAsB,IAAI,IAAI,MAAM,CAAC;AAC5D,OAAI,KACF,gBAAgB,KAAK,SAAS,KAAK,SAAS,MAAM,SAAS,QAAQ,SAAS,GAAG,CAChF;AACD;;AAGF,MAAI,CAAC,IAAI,QAAQ,IAAI,KAAK,WAAW,EACnC,OAAM,IAAI,MAAM,uBAAuB,IAAI,QAAQ,OAAO,2BAA2B;EAIvF,MAAM,QAAQ,MAAM,gBADR,8BAA8B,IAAI,KACP,EAAE;GACvC,aAAa,IAAI;GACjB,QAAQ;GACR;GACA,kBAAkB,IAAI;GACvB,CAAC;AAEF,MAAI,MAAM;GAAE,KAAK,MAAM;GAAK,OAAO,MAAM;GAAM,MAAM,IAAI;GAAM,EAAE,+BAA+B;AAEhG,MAAI,KAAK,WAAW,KAAK,MAAM,CAAC;;AAGlC,QAAO,IAAI,SAAS,MAAM,KAAA;;;AAI5B,eAAsB,4BAA4B,KAA8B;CAC9E,MAAM,EAAE,WAAW,MAAM,mBAAmB,IAAI;AAChD,QAAO;;AAGT,SAAgB,eAAe,KAAmB;AAChD,eAAc,IAAI"}
@@ -0,0 +1,13 @@
1
+ import type { InboundAttachmentInput, MediaRef } from '../../media/types.js';
2
+ import { isImageInboundAttachment, isVoiceInboundAttachment } from './attachment-classifiers.js';
3
+ export type { InboundAttachmentInput, MediaRef } from '../../media/types.js';
4
+ export { isImageInboundAttachment, isVoiceInboundAttachment };
5
+ export declare function toMediaRef(att: InboundAttachmentInput, saved: {
6
+ id: string;
7
+ bucket: MediaRef['bucket'];
8
+ uri: string;
9
+ path: string;
10
+ size: number;
11
+ contentType: string;
12
+ }): MediaRef;
13
+ export declare function mediaRefFromUri(att: InboundAttachmentInput, uri: string, path: string, bucket: MediaRef['bucket'], id: string): MediaRef;
@@ -0,0 +1,30 @@
1
+ import { isImageInboundAttachment, isVoiceInboundAttachment } from "./attachment-classifiers.js";
2
+ //#region src/channels/attachments/inbound-types.ts
3
+ function toMediaRef(att, saved) {
4
+ return {
5
+ id: saved.id,
6
+ bucket: saved.bucket,
7
+ type: att.type,
8
+ mimeType: att.mimeType?.trim() || saved.contentType,
9
+ name: att.name?.trim() || "file",
10
+ size: att.size ?? saved.size,
11
+ uri: saved.uri,
12
+ path: saved.path
13
+ };
14
+ }
15
+ function mediaRefFromUri(att, uri, path, bucket, id) {
16
+ return {
17
+ id,
18
+ bucket,
19
+ type: att.type,
20
+ mimeType: att.mimeType?.trim() || "application/octet-stream",
21
+ name: att.name?.trim() || "file",
22
+ size: att.size ?? 0,
23
+ uri,
24
+ path
25
+ };
26
+ }
27
+ //#endregion
28
+ export { isImageInboundAttachment, isVoiceInboundAttachment, mediaRefFromUri, toMediaRef };
29
+
30
+ //# sourceMappingURL=inbound-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbound-types.js","names":[],"sources":["../../../../src/channels/attachments/inbound-types.ts"],"sourcesContent":["import type { InboundAttachmentInput, MediaRef } from '../../media/types.js';\nimport {\n isImageInboundAttachment,\n isVoiceInboundAttachment,\n} from './attachment-classifiers.js';\n\nexport type { InboundAttachmentInput, MediaRef } from '../../media/types.js';\nexport { isImageInboundAttachment, isVoiceInboundAttachment };\n\nexport function toMediaRef(\n att: InboundAttachmentInput,\n saved: { id: string; bucket: MediaRef['bucket']; uri: string; path: string; size: number; contentType: string },\n): MediaRef {\n return {\n id: saved.id,\n bucket: saved.bucket,\n type: att.type,\n mimeType: att.mimeType?.trim() || saved.contentType,\n name: att.name?.trim() || 'file',\n size: att.size ?? saved.size,\n uri: saved.uri,\n path: saved.path,\n };\n}\n\nexport function mediaRefFromUri(\n att: InboundAttachmentInput,\n uri: string,\n path: string,\n bucket: MediaRef['bucket'],\n id: string,\n): MediaRef {\n return {\n id,\n bucket,\n type: att.type,\n mimeType: att.mimeType?.trim() || 'application/octet-stream',\n name: att.name?.trim() || 'file',\n size: att.size ?? 0,\n uri,\n path,\n };\n}\n"],"mappings":";;AASA,SAAgB,WACd,KACA,OACU;AACV,QAAO;EACL,IAAI,MAAM;EACV,QAAQ,MAAM;EACd,MAAM,IAAI;EACV,UAAU,IAAI,UAAU,MAAM,IAAI,MAAM;EACxC,MAAM,IAAI,MAAM,MAAM,IAAI;EAC1B,MAAM,IAAI,QAAQ,MAAM;EACxB,KAAK,MAAM;EACX,MAAM,MAAM;EACb;;AAGH,SAAgB,gBACd,KACA,KACA,MACA,QACA,IACU;AACV,QAAO;EACL;EACA;EACA,MAAM,IAAI;EACV,UAAU,IAAI,UAAU,MAAM,IAAI;EAClC,MAAM,IAAI,MAAM,MAAM,IAAI;EAC1B,MAAM,IAAI,QAAQ;EAClB;EACA;EACD"}
@@ -1,14 +1,5 @@
1
1
  /**
2
- * Persist outbound TTS audio under `<agentHome>/tts/<session>/`.
2
+ * Persist outbound TTS audio to `{stateDir}/media/tts/`.
3
3
  */
4
- import type { InternalAttachmentRoots } from './inbound-persist.js';
5
- export declare const TTS_REL_ROOT = "tts";
6
- export declare function persistOutboundTtsAudio(agentHomeRoot: string, sessionKey: string, audioBuffer: Buffer, format: string): Promise<{
7
- workspaceRelativePath: string;
8
- name: string;
9
- size: number;
10
- }>;
11
- /**
12
- * Resolve a stored relative path under `tts/`.
13
- */
14
- export declare function resolveSafeTtsFilePath(roots: InternalAttachmentRoots, relRaw: string): string | null;
4
+ import type { MediaRef } from '../../media/types.js';
5
+ export declare function persistOutboundTtsAudio(audioBuffer: Buffer, format: string): Promise<MediaRef>;
@@ -1,66 +1,46 @@
1
1
  import { createLogger } from "../../utils/logger/index.js";
2
2
  import { init_logger } from "../../utils/logger.js";
3
- import { mkdir, writeFile } from "fs/promises";
4
- import { join, resolve } from "path";
5
- import { randomBytes } from "crypto";
3
+ import { saveMediaBuffer } from "../../media/store.js";
6
4
  //#region src/channels/attachments/outbound-tts-persist.ts
7
- /**
8
- * Persist outbound TTS audio under `<agentHome>/tts/<session>/`.
9
- */
10
5
  init_logger();
11
6
  const log = createLogger("OutboundTtsPersist");
12
- const TTS_REL_ROOT = "tts";
13
- function sanitizeSessionSegment(sessionKey) {
14
- return sessionKey.replace(/[^a-zA-Z0-9_.-]+/g, "_").slice(0, 180) || "session";
15
- }
16
7
  function extForFormat(format) {
17
8
  const f = format.toLowerCase();
18
- if (f === "opus" || f === "ogg") return "ogg";
19
- if (f === "mp3" || f === "mpeg") return "mp3";
20
- if (f === "wav") return "wav";
21
- return "bin";
9
+ if (f === "opus" || f === "ogg") return ".ogg";
10
+ if (f === "mp3" || f === "mpeg") return ".mp3";
11
+ if (f === "wav") return ".wav";
12
+ return ".bin";
22
13
  }
23
- function resolveUnderRoot(root, rel, requiredPrefix) {
24
- const normalized = rel.replace(/\\/g, "/").replace(/^\/+/, "");
25
- if (normalized.includes("..") || !normalized.startsWith(requiredPrefix)) return null;
26
- const abs = resolve(root, ...normalized.split("/"));
27
- const rootResolved = resolve(root);
28
- if (!abs.startsWith(rootResolved)) return null;
29
- return abs;
14
+ function mimeForFormat(format) {
15
+ const f = format.toLowerCase();
16
+ if (f === "opus" || f === "ogg") return "audio/ogg";
17
+ if (f === "mp3" || f === "mpeg") return "audio/mpeg";
18
+ if (f === "wav") return "audio/wav";
19
+ return "application/octet-stream";
30
20
  }
31
- async function persistOutboundTtsAudio(agentHomeRoot, sessionKey, audioBuffer, format) {
32
- const sessionSeg = sanitizeSessionSegment(sessionKey);
33
- const dirAbs = resolve(agentHomeRoot, "tts", sessionSeg);
34
- await mkdir(dirAbs, { recursive: true });
21
+ async function persistOutboundTtsAudio(audioBuffer, format) {
35
22
  const ext = extForFormat(format);
36
- const fname = `assist_${Date.now()}_${randomBytes(4).toString("hex")}.${ext}`;
37
- await writeFile(join(dirAbs, fname), audioBuffer);
38
- const workspaceRelativePath = [
39
- "tts",
40
- sessionSeg,
41
- fname
42
- ].join("/");
23
+ const saved = await saveMediaBuffer(audioBuffer, {
24
+ contentType: mimeForFormat(format),
25
+ bucket: "tts",
26
+ originalFilename: `assist${ext}`
27
+ });
43
28
  log.debug({
44
- sessionKey,
45
- workspaceRelativePath,
46
- bytes: audioBuffer.length
29
+ uri: saved.uri,
30
+ bytes: saved.size
47
31
  }, "TTS audio persisted");
48
32
  return {
49
- workspaceRelativePath,
50
- name: fname,
51
- size: audioBuffer.length
33
+ id: saved.id,
34
+ bucket: "tts",
35
+ type: "voice",
36
+ mimeType: saved.contentType,
37
+ name: saved.id,
38
+ size: saved.size,
39
+ uri: saved.uri,
40
+ path: saved.path
52
41
  };
53
42
  }
54
- /**
55
- * Resolve a stored relative path under `tts/`.
56
- */
57
- function resolveSafeTtsFilePath(roots, relRaw) {
58
- const rel = relRaw.replace(/\\/g, "/").replace(/^\/+/, "");
59
- if (rel.includes("..")) return null;
60
- if (rel.startsWith(`tts/`)) return resolveUnderRoot(roots.agentHome, rel, `tts/`);
61
- return null;
62
- }
63
43
  //#endregion
64
- export { TTS_REL_ROOT, persistOutboundTtsAudio, resolveSafeTtsFilePath };
44
+ export { persistOutboundTtsAudio };
65
45
 
66
46
  //# sourceMappingURL=outbound-tts-persist.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"outbound-tts-persist.js","names":[],"sources":["../../../../src/channels/attachments/outbound-tts-persist.ts"],"sourcesContent":["/**\n * Persist outbound TTS audio under `<agentHome>/tts/<session>/`.\n */\n\nimport { mkdir, writeFile } from 'fs/promises';\nimport { join, resolve } from 'path';\nimport { randomBytes } from 'crypto';\nimport { createLogger } from '../../utils/logger.js';\nimport type { InternalAttachmentRoots } from './inbound-persist.js';\n\nconst log = createLogger('OutboundTtsPersist');\n\nexport const TTS_REL_ROOT = 'tts';\n\nfunction sanitizeSessionSegment(sessionKey: string): string {\n return sessionKey.replace(/[^a-zA-Z0-9_.-]+/g, '_').slice(0, 180) || 'session';\n}\n\nfunction extForFormat(format: string): string {\n const f = format.toLowerCase();\n if (f === 'opus' || f === 'ogg') return 'ogg';\n if (f === 'mp3' || f === 'mpeg') return 'mp3';\n if (f === 'wav') return 'wav';\n return 'bin';\n}\n\nfunction resolveUnderRoot(root: string, rel: string, requiredPrefix: string): string | null {\n const normalized = rel.replace(/\\\\/g, '/').replace(/^\\/+/, '');\n if (normalized.includes('..') || !normalized.startsWith(requiredPrefix)) {\n return null;\n }\n const abs = resolve(root, ...normalized.split('/'));\n const rootResolved = resolve(root);\n if (!abs.startsWith(rootResolved)) {\n return null;\n }\n return abs;\n}\n\nexport async function persistOutboundTtsAudio(\n agentHomeRoot: string,\n sessionKey: string,\n audioBuffer: Buffer,\n format: string,\n): Promise<{\n workspaceRelativePath: string;\n name: string;\n size: number;\n}> {\n const sessionSeg = sanitizeSessionSegment(sessionKey);\n const dirAbs = resolve(agentHomeRoot, TTS_REL_ROOT, sessionSeg);\n await mkdir(dirAbs, { recursive: true });\n const ext = extForFormat(format);\n const fname = `assist_${Date.now()}_${randomBytes(4).toString('hex')}.${ext}`;\n const absFile = join(dirAbs, fname);\n await writeFile(absFile, audioBuffer);\n const workspaceRelativePath = [TTS_REL_ROOT, sessionSeg, fname].join('/');\n log.debug({ sessionKey, workspaceRelativePath, bytes: audioBuffer.length }, 'TTS audio persisted');\n return {\n workspaceRelativePath,\n name: fname,\n size: audioBuffer.length,\n };\n}\n\n/**\n * Resolve a stored relative path under `tts/`.\n */\nexport function resolveSafeTtsFilePath(roots: InternalAttachmentRoots, relRaw: string): string | null {\n const rel = relRaw.replace(/\\\\/g, '/').replace(/^\\/+/, '');\n if (rel.includes('..')) {\n return null;\n }\n\n if (rel.startsWith(`${TTS_REL_ROOT}/`)) {\n return resolveUnderRoot(roots.agentHome, rel, `${TTS_REL_ROOT}/`);\n }\n\n return null;\n}\n"],"mappings":";;;;;;;;;aAOqD;AAGrD,MAAM,MAAM,aAAa,qBAAqB;AAE9C,MAAa,eAAe;AAE5B,SAAS,uBAAuB,YAA4B;AAC1D,QAAO,WAAW,QAAQ,qBAAqB,IAAI,CAAC,MAAM,GAAG,IAAI,IAAI;;AAGvE,SAAS,aAAa,QAAwB;CAC5C,MAAM,IAAI,OAAO,aAAa;AAC9B,KAAI,MAAM,UAAU,MAAM,MAAO,QAAO;AACxC,KAAI,MAAM,SAAS,MAAM,OAAQ,QAAO;AACxC,KAAI,MAAM,MAAO,QAAO;AACxB,QAAO;;AAGT,SAAS,iBAAiB,MAAc,KAAa,gBAAuC;CAC1F,MAAM,aAAa,IAAI,QAAQ,OAAO,IAAI,CAAC,QAAQ,QAAQ,GAAG;AAC9D,KAAI,WAAW,SAAS,KAAK,IAAI,CAAC,WAAW,WAAW,eAAe,CACrE,QAAO;CAET,MAAM,MAAM,QAAQ,MAAM,GAAG,WAAW,MAAM,IAAI,CAAC;CACnD,MAAM,eAAe,QAAQ,KAAK;AAClC,KAAI,CAAC,IAAI,WAAW,aAAa,CAC/B,QAAO;AAET,QAAO;;AAGT,eAAsB,wBACpB,eACA,YACA,aACA,QAKC;CACD,MAAM,aAAa,uBAAuB,WAAW;CACrD,MAAM,SAAS,QAAQ,eAAA,OAA6B,WAAW;AAC/D,OAAM,MAAM,QAAQ,EAAE,WAAW,MAAM,CAAC;CACxC,MAAM,MAAM,aAAa,OAAO;CAChC,MAAM,QAAQ,UAAU,KAAK,KAAK,CAAC,GAAG,YAAY,EAAE,CAAC,SAAS,MAAM,CAAC,GAAG;AAExE,OAAM,UADU,KAAK,QAAQ,MACN,EAAE,YAAY;CACrC,MAAM,wBAAwB;;EAAe;EAAY;EAAM,CAAC,KAAK,IAAI;AACzE,KAAI,MAAM;EAAE;EAAY;EAAuB,OAAO,YAAY;EAAQ,EAAE,sBAAsB;AAClG,QAAO;EACL;EACA,MAAM;EACN,MAAM,YAAY;EACnB;;;;;AAMH,SAAgB,uBAAuB,OAAgC,QAA+B;CACpG,MAAM,MAAM,OAAO,QAAQ,OAAO,IAAI,CAAC,QAAQ,QAAQ,GAAG;AAC1D,KAAI,IAAI,SAAS,KAAK,CACpB,QAAO;AAGT,KAAI,IAAI,WAAW,OAAmB,CACpC,QAAO,iBAAiB,MAAM,WAAW,KAAK,OAAmB;AAGnE,QAAO"}
1
+ {"version":3,"file":"outbound-tts-persist.js","names":[],"sources":["../../../../src/channels/attachments/outbound-tts-persist.ts"],"sourcesContent":["/**\n * Persist outbound TTS audio to `{stateDir}/media/tts/`.\n */\n\nimport { createLogger } from '../../utils/logger.js';\nimport { saveMediaBuffer } from '../../media/index.js';\nimport type { MediaRef } from '../../media/types.js';\n\nconst log = createLogger('OutboundTtsPersist');\n\nfunction extForFormat(format: string): string {\n const f = format.toLowerCase();\n if (f === 'opus' || f === 'ogg') return '.ogg';\n if (f === 'mp3' || f === 'mpeg') return '.mp3';\n if (f === 'wav') return '.wav';\n return '.bin';\n}\n\nfunction mimeForFormat(format: string): string {\n const f = format.toLowerCase();\n if (f === 'opus' || f === 'ogg') return 'audio/ogg';\n if (f === 'mp3' || f === 'mpeg') return 'audio/mpeg';\n if (f === 'wav') return 'audio/wav';\n return 'application/octet-stream';\n}\n\nexport async function persistOutboundTtsAudio(\n audioBuffer: Buffer,\n format: string,\n): Promise<MediaRef> {\n const ext = extForFormat(format);\n const saved = await saveMediaBuffer(audioBuffer, {\n contentType: mimeForFormat(format),\n bucket: 'tts',\n originalFilename: `assist${ext}`,\n });\n\n log.debug({ uri: saved.uri, bytes: saved.size }, 'TTS audio persisted');\n\n return {\n id: saved.id,\n bucket: 'tts',\n type: 'voice',\n mimeType: saved.contentType,\n name: saved.id,\n size: saved.size,\n uri: saved.uri,\n path: saved.path,\n };\n}\n"],"mappings":";;;;aAIqD;AAIrD,MAAM,MAAM,aAAa,qBAAqB;AAE9C,SAAS,aAAa,QAAwB;CAC5C,MAAM,IAAI,OAAO,aAAa;AAC9B,KAAI,MAAM,UAAU,MAAM,MAAO,QAAO;AACxC,KAAI,MAAM,SAAS,MAAM,OAAQ,QAAO;AACxC,KAAI,MAAM,MAAO,QAAO;AACxB,QAAO;;AAGT,SAAS,cAAc,QAAwB;CAC7C,MAAM,IAAI,OAAO,aAAa;AAC9B,KAAI,MAAM,UAAU,MAAM,MAAO,QAAO;AACxC,KAAI,MAAM,SAAS,MAAM,OAAQ,QAAO;AACxC,KAAI,MAAM,MAAO,QAAO;AACxB,QAAO;;AAGT,eAAsB,wBACpB,aACA,QACmB;CACnB,MAAM,MAAM,aAAa,OAAO;CAChC,MAAM,QAAQ,MAAM,gBAAgB,aAAa;EAC/C,aAAa,cAAc,OAAO;EAClC,QAAQ;EACR,kBAAkB,SAAS;EAC5B,CAAC;AAEF,KAAI,MAAM;EAAE,KAAK,MAAM;EAAK,OAAO,MAAM;EAAM,EAAE,sBAAsB;AAEvE,QAAO;EACL,IAAI,MAAM;EACV,QAAQ;EACR,MAAM;EACN,UAAU,MAAM;EAChB,MAAM,MAAM;EACZ,MAAM,MAAM;EACZ,KAAK,MAAM;EACX,MAAM,MAAM;EACb"}
@@ -2,12 +2,12 @@
2
2
  * STT for webchat voice attachments: merge transcripts into user text and track inbound voice for TTS trigger.
3
3
  */
4
4
  import type { STTConfig } from '../../voice/stt/types.js';
5
- import { type InboundAttachmentInput, type InternalAttachmentRoots } from './inbound-persist.js';
5
+ import { type InboundAttachmentInput, type MediaRef } from './inbound-persist.js';
6
6
  export declare function mergeSttConfigFromAppConfig(stt: Partial<STTConfig> | undefined, toolsMedia?: {
7
7
  models?: STTConfig['sharedModels'];
8
8
  }): STTConfig;
9
- export declare function isVoiceLikeAttachment(att: InboundAttachmentInput): boolean;
10
- export declare function mergeVoiceTranscriptsIntoUserText(attachmentRoots: InternalAttachmentRoots, prepared: InboundAttachmentInput[] | undefined, userText: string, sttConfig: STTConfig, opts?: {
9
+ export declare function isVoiceLikeAttachment(att: InboundAttachmentInput | MediaRef): boolean;
10
+ export declare function mergeVoiceTranscriptsIntoUserText(prepared: (InboundAttachmentInput | MediaRef)[] | undefined, userText: string, sttConfig: STTConfig, opts?: {
11
11
  skipVoiceTranscription?: boolean;
12
12
  }): Promise<{
13
13
  text: string;
@@ -1,13 +1,9 @@
1
- import { decodeInboundAttachmentBase64, resolveSafeInboundFilePath } from "./inbound-persist.js";
1
+ import { decodeInboundAttachmentBase64, readInboundAttachmentBuffer } from "./inbound-persist.js";
2
2
  import { DEFAULT_STT_CONFIG } from "../../voice/stt/types.js";
3
3
  import { transcribe } from "../../voice/stt/transcribe-core.js";
4
4
  import { isSTTAvailable } from "../../voice/stt/availability.js";
5
5
  import "../../voice/stt/index.js";
6
- import { readFile } from "fs/promises";
7
6
  //#region src/channels/attachments/voice-stt-webchat.ts
8
- /**
9
- * STT for webchat voice attachments: merge transcripts into user text and track inbound voice for TTS trigger.
10
- */
11
7
  const STT_MAX_BYTES = 25 * 1024 * 1024;
12
8
  function mergeSttProviders(base, patch) {
13
9
  if (!base && !patch) return void 0;
@@ -35,7 +31,7 @@ function isVoiceLikeAttachment(att) {
35
31
  if (att.type === "voice") return true;
36
32
  return (att.mimeType?.toLowerCase() ?? "").startsWith("audio/");
37
33
  }
38
- async function mergeVoiceTranscriptsIntoUserText(attachmentRoots, prepared, userText, sttConfig, opts) {
34
+ async function mergeVoiceTranscriptsIntoUserText(prepared, userText, sttConfig, opts) {
39
35
  if (!prepared?.length) return {
40
36
  text: userText,
41
37
  inboundVoice: false,
@@ -60,14 +56,12 @@ async function mergeVoiceTranscriptsIntoUserText(attachmentRoots, prepared, user
60
56
  for (const att of prepared) {
61
57
  if (!isVoiceLikeAttachment(att)) continue;
62
58
  let buf = null;
63
- if (att.workspaceRelativePath) {
64
- const abs = resolveSafeInboundFilePath(attachmentRoots, att.workspaceRelativePath);
65
- if (abs) try {
66
- buf = await readFile(abs);
67
- } catch {
68
- buf = null;
69
- }
70
- } else if (att.data) try {
59
+ if ("uri" in att && att.uri?.trim()) try {
60
+ buf = await readInboundAttachmentBuffer(att.uri.trim());
61
+ } catch {
62
+ buf = null;
63
+ }
64
+ else if ("data" in att && att.data) try {
71
65
  buf = decodeInboundAttachmentBase64(att.data);
72
66
  } catch {
73
67
  buf = null;
@@ -1 +1 @@
1
- {"version":3,"file":"voice-stt-webchat.js","names":[],"sources":["../../../../src/channels/attachments/voice-stt-webchat.ts"],"sourcesContent":["/**\n * STT for webchat voice attachments: merge transcripts into user text and track inbound voice for TTS trigger.\n */\n\nimport { readFile } from 'fs/promises';\n\nimport type { STTConfig } from '../../voice/stt/types.js';\nimport { DEFAULT_STT_CONFIG } from '../../voice/stt/types.js';\nimport { isSTTAvailable, transcribe } from '../../voice/stt/index.js';\nimport {\n resolveSafeInboundFilePath,\n type InboundAttachmentInput,\n type InternalAttachmentRoots,\n decodeInboundAttachmentBase64,\n} from './inbound-persist.js';\n\nconst STT_MAX_BYTES = 25 * 1024 * 1024;\n\nfunction mergeSttProviders(\n base: STTConfig['providers'],\n patch: STTConfig['providers'],\n): STTConfig['providers'] {\n if (!base && !patch) return undefined;\n const merged: Record<string, Record<string, unknown>> = { ...(base ?? {}) };\n for (const [id, slice] of Object.entries(patch ?? {})) {\n merged[id] = { ...(merged[id] ?? {}), ...slice };\n }\n return merged;\n}\n\nexport function mergeSttConfigFromAppConfig(\n stt: Partial<STTConfig> | undefined,\n toolsMedia?: { models?: STTConfig['sharedModels'] },\n): STTConfig {\n const p = stt ?? {};\n return {\n ...DEFAULT_STT_CONFIG,\n ...p,\n providers: mergeSttProviders(DEFAULT_STT_CONFIG.providers, p.providers),\n fallback: { ...DEFAULT_STT_CONFIG.fallback!, ...p.fallback },\n ...(toolsMedia?.models?.length ? { sharedModels: toolsMedia.models } : {}),\n };\n}\n\nexport function isVoiceLikeAttachment(att: InboundAttachmentInput): boolean {\n if (att.type === 'voice') return true;\n const m = att.mimeType?.toLowerCase() ?? '';\n return m.startsWith('audio/');\n}\n\nexport async function mergeVoiceTranscriptsIntoUserText(\n attachmentRoots: InternalAttachmentRoots,\n prepared: InboundAttachmentInput[] | undefined,\n userText: string,\n sttConfig: STTConfig,\n opts?: { skipVoiceTranscription?: boolean },\n): Promise<{ text: string; inboundVoice: boolean; voiceTranscripts: string[] }> {\n if (!prepared?.length) {\n return { text: userText, inboundVoice: false, voiceTranscripts: [] };\n }\n\n const hasVoice = prepared.some(isVoiceLikeAttachment);\n if (!hasVoice) {\n return { text: userText, inboundVoice: false, voiceTranscripts: [] };\n }\n\n if (opts?.skipVoiceTranscription === true) {\n return { text: userText, inboundVoice: true, voiceTranscripts: [] };\n }\n\n if (!isSTTAvailable(sttConfig)) {\n return { text: userText, inboundVoice: true, voiceTranscripts: [] };\n }\n\n const transcripts: string[] = [];\n\n for (const att of prepared) {\n if (!isVoiceLikeAttachment(att)) continue;\n\n let buf: Buffer | null = null;\n if (att.workspaceRelativePath) {\n const abs = resolveSafeInboundFilePath(attachmentRoots, att.workspaceRelativePath);\n if (abs) {\n try {\n buf = await readFile(abs);\n } catch {\n buf = null;\n }\n }\n } else if (att.data) {\n try {\n buf = decodeInboundAttachmentBase64(att.data);\n } catch {\n buf = null;\n }\n }\n\n if (!buf || buf.length === 0) {\n transcripts.push('[Voice: empty]');\n continue;\n }\n if (buf.length > STT_MAX_BYTES) {\n transcripts.push('[Voice: file too large]');\n continue;\n }\n\n try {\n const r = await transcribe(buf, sttConfig, {\n language: sttConfig.provider === 'alibaba' ? 'zh' : undefined,\n });\n transcripts.push(r.text.trim() || '[Voice: no speech detected]');\n } catch {\n transcripts.push('[STT failed]');\n }\n }\n\n const merged = [transcripts.filter(Boolean).join('\\n'), userText.trim()].filter(Boolean).join('\\n\\n');\n return { text: merged || userText, inboundVoice: true, voiceTranscripts: transcripts };\n}\n"],"mappings":";;;;;;;;;;AAgBA,MAAM,gBAAgB,KAAK,OAAO;AAElC,SAAS,kBACP,MACA,OACwB;AACxB,KAAI,CAAC,QAAQ,CAAC,MAAO,QAAO,KAAA;CAC5B,MAAM,SAAkD,EAAE,GAAI,QAAQ,EAAE,EAAG;AAC3E,MAAK,MAAM,CAAC,IAAI,UAAU,OAAO,QAAQ,SAAS,EAAE,CAAC,CACnD,QAAO,MAAM;EAAE,GAAI,OAAO,OAAO,EAAE;EAAG,GAAG;EAAO;AAElD,QAAO;;AAGT,SAAgB,4BACd,KACA,YACW;CACX,MAAM,IAAI,OAAO,EAAE;AACnB,QAAO;EACL,GAAG;EACH,GAAG;EACH,WAAW,kBAAkB,mBAAmB,WAAW,EAAE,UAAU;EACvE,UAAU;GAAE,GAAG,mBAAmB;GAAW,GAAG,EAAE;GAAU;EAC5D,GAAI,YAAY,QAAQ,SAAS,EAAE,cAAc,WAAW,QAAQ,GAAG,EAAE;EAC1E;;AAGH,SAAgB,sBAAsB,KAAsC;AAC1E,KAAI,IAAI,SAAS,QAAS,QAAO;AAEjC,SADU,IAAI,UAAU,aAAa,IAAI,IAChC,WAAW,SAAS;;AAG/B,eAAsB,kCACpB,iBACA,UACA,UACA,WACA,MAC8E;AAC9E,KAAI,CAAC,UAAU,OACb,QAAO;EAAE,MAAM;EAAU,cAAc;EAAO,kBAAkB,EAAE;EAAE;AAItE,KAAI,CADa,SAAS,KAAK,sBAClB,CACX,QAAO;EAAE,MAAM;EAAU,cAAc;EAAO,kBAAkB,EAAE;EAAE;AAGtE,KAAI,MAAM,2BAA2B,KACnC,QAAO;EAAE,MAAM;EAAU,cAAc;EAAM,kBAAkB,EAAE;EAAE;AAGrE,KAAI,CAAC,eAAe,UAAU,CAC5B,QAAO;EAAE,MAAM;EAAU,cAAc;EAAM,kBAAkB,EAAE;EAAE;CAGrE,MAAM,cAAwB,EAAE;AAEhC,MAAK,MAAM,OAAO,UAAU;AAC1B,MAAI,CAAC,sBAAsB,IAAI,CAAE;EAEjC,IAAI,MAAqB;AACzB,MAAI,IAAI,uBAAuB;GAC7B,MAAM,MAAM,2BAA2B,iBAAiB,IAAI,sBAAsB;AAClF,OAAI,IACF,KAAI;AACF,UAAM,MAAM,SAAS,IAAI;WACnB;AACN,UAAM;;aAGD,IAAI,KACb,KAAI;AACF,SAAM,8BAA8B,IAAI,KAAK;UACvC;AACN,SAAM;;AAIV,MAAI,CAAC,OAAO,IAAI,WAAW,GAAG;AAC5B,eAAY,KAAK,iBAAiB;AAClC;;AAEF,MAAI,IAAI,SAAS,eAAe;AAC9B,eAAY,KAAK,0BAA0B;AAC3C;;AAGF,MAAI;GACF,MAAM,IAAI,MAAM,WAAW,KAAK,WAAW,EACzC,UAAU,UAAU,aAAa,YAAY,OAAO,KAAA,GACrD,CAAC;AACF,eAAY,KAAK,EAAE,KAAK,MAAM,IAAI,8BAA8B;UAC1D;AACN,eAAY,KAAK,eAAe;;;AAKpC,QAAO;EAAE,MADM,CAAC,YAAY,OAAO,QAAQ,CAAC,KAAK,KAAK,EAAE,SAAS,MAAM,CAAC,CAAC,OAAO,QAAQ,CAAC,KAAK,OACzE,IAAI;EAAU,cAAc;EAAM,kBAAkB;EAAa"}
1
+ {"version":3,"file":"voice-stt-webchat.js","names":[],"sources":["../../../../src/channels/attachments/voice-stt-webchat.ts"],"sourcesContent":["/**\n * STT for webchat voice attachments: merge transcripts into user text and track inbound voice for TTS trigger.\n */\n\nimport type { STTConfig } from '../../voice/stt/types.js';\nimport { DEFAULT_STT_CONFIG } from '../../voice/stt/types.js';\nimport { isSTTAvailable, transcribe } from '../../voice/stt/index.js';\nimport {\n decodeInboundAttachmentBase64,\n readInboundAttachmentBuffer,\n type InboundAttachmentInput,\n type MediaRef,\n} from './inbound-persist.js';\n\nconst STT_MAX_BYTES = 25 * 1024 * 1024;\n\nfunction mergeSttProviders(\n base: STTConfig['providers'],\n patch: STTConfig['providers'],\n): STTConfig['providers'] {\n if (!base && !patch) return undefined;\n const merged: Record<string, Record<string, unknown>> = { ...(base ?? {}) };\n for (const [id, slice] of Object.entries(patch ?? {})) {\n merged[id] = { ...(merged[id] ?? {}), ...slice };\n }\n return merged;\n}\n\nexport function mergeSttConfigFromAppConfig(\n stt: Partial<STTConfig> | undefined,\n toolsMedia?: { models?: STTConfig['sharedModels'] },\n): STTConfig {\n const p = stt ?? {};\n return {\n ...DEFAULT_STT_CONFIG,\n ...p,\n providers: mergeSttProviders(DEFAULT_STT_CONFIG.providers, p.providers),\n fallback: { ...DEFAULT_STT_CONFIG.fallback!, ...p.fallback },\n ...(toolsMedia?.models?.length ? { sharedModels: toolsMedia.models } : {}),\n };\n}\n\nexport function isVoiceLikeAttachment(att: InboundAttachmentInput | MediaRef): boolean {\n if (att.type === 'voice') return true;\n const m = att.mimeType?.toLowerCase() ?? '';\n return m.startsWith('audio/');\n}\n\nexport async function mergeVoiceTranscriptsIntoUserText(\n prepared: (InboundAttachmentInput | MediaRef)[] | undefined,\n userText: string,\n sttConfig: STTConfig,\n opts?: { skipVoiceTranscription?: boolean },\n): Promise<{ text: string; inboundVoice: boolean; voiceTranscripts: string[] }> {\n if (!prepared?.length) {\n return { text: userText, inboundVoice: false, voiceTranscripts: [] };\n }\n\n const hasVoice = prepared.some(isVoiceLikeAttachment);\n if (!hasVoice) {\n return { text: userText, inboundVoice: false, voiceTranscripts: [] };\n }\n\n if (opts?.skipVoiceTranscription === true) {\n return { text: userText, inboundVoice: true, voiceTranscripts: [] };\n }\n\n if (!isSTTAvailable(sttConfig)) {\n return { text: userText, inboundVoice: true, voiceTranscripts: [] };\n }\n\n const transcripts: string[] = [];\n\n for (const att of prepared) {\n if (!isVoiceLikeAttachment(att)) continue;\n\n let buf: Buffer | null = null;\n if ('uri' in att && att.uri?.trim()) {\n try {\n buf = await readInboundAttachmentBuffer(att.uri.trim());\n } catch {\n buf = null;\n }\n } else if ('data' in att && att.data) {\n try {\n buf = decodeInboundAttachmentBase64(att.data);\n } catch {\n buf = null;\n }\n }\n\n if (!buf || buf.length === 0) {\n transcripts.push('[Voice: empty]');\n continue;\n }\n if (buf.length > STT_MAX_BYTES) {\n transcripts.push('[Voice: file too large]');\n continue;\n }\n\n try {\n const r = await transcribe(buf, sttConfig, {\n language: sttConfig.provider === 'alibaba' ? 'zh' : undefined,\n });\n transcripts.push(r.text.trim() || '[Voice: no speech detected]');\n } catch {\n transcripts.push('[STT failed]');\n }\n }\n\n const merged = [transcripts.filter(Boolean).join('\\n'), userText.trim()].filter(Boolean).join('\\n\\n');\n return { text: merged || userText, inboundVoice: true, voiceTranscripts: transcripts };\n}\n"],"mappings":";;;;;;AAcA,MAAM,gBAAgB,KAAK,OAAO;AAElC,SAAS,kBACP,MACA,OACwB;AACxB,KAAI,CAAC,QAAQ,CAAC,MAAO,QAAO,KAAA;CAC5B,MAAM,SAAkD,EAAE,GAAI,QAAQ,EAAE,EAAG;AAC3E,MAAK,MAAM,CAAC,IAAI,UAAU,OAAO,QAAQ,SAAS,EAAE,CAAC,CACnD,QAAO,MAAM;EAAE,GAAI,OAAO,OAAO,EAAE;EAAG,GAAG;EAAO;AAElD,QAAO;;AAGT,SAAgB,4BACd,KACA,YACW;CACX,MAAM,IAAI,OAAO,EAAE;AACnB,QAAO;EACL,GAAG;EACH,GAAG;EACH,WAAW,kBAAkB,mBAAmB,WAAW,EAAE,UAAU;EACvE,UAAU;GAAE,GAAG,mBAAmB;GAAW,GAAG,EAAE;GAAU;EAC5D,GAAI,YAAY,QAAQ,SAAS,EAAE,cAAc,WAAW,QAAQ,GAAG,EAAE;EAC1E;;AAGH,SAAgB,sBAAsB,KAAiD;AACrF,KAAI,IAAI,SAAS,QAAS,QAAO;AAEjC,SADU,IAAI,UAAU,aAAa,IAAI,IAChC,WAAW,SAAS;;AAG/B,eAAsB,kCACpB,UACA,UACA,WACA,MAC8E;AAC9E,KAAI,CAAC,UAAU,OACb,QAAO;EAAE,MAAM;EAAU,cAAc;EAAO,kBAAkB,EAAE;EAAE;AAItE,KAAI,CADa,SAAS,KAAK,sBAClB,CACX,QAAO;EAAE,MAAM;EAAU,cAAc;EAAO,kBAAkB,EAAE;EAAE;AAGtE,KAAI,MAAM,2BAA2B,KACnC,QAAO;EAAE,MAAM;EAAU,cAAc;EAAM,kBAAkB,EAAE;EAAE;AAGrE,KAAI,CAAC,eAAe,UAAU,CAC5B,QAAO;EAAE,MAAM;EAAU,cAAc;EAAM,kBAAkB,EAAE;EAAE;CAGrE,MAAM,cAAwB,EAAE;AAEhC,MAAK,MAAM,OAAO,UAAU;AAC1B,MAAI,CAAC,sBAAsB,IAAI,CAAE;EAEjC,IAAI,MAAqB;AACzB,MAAI,SAAS,OAAO,IAAI,KAAK,MAAM,CACjC,KAAI;AACF,SAAM,MAAM,4BAA4B,IAAI,IAAI,MAAM,CAAC;UACjD;AACN,SAAM;;WAEC,UAAU,OAAO,IAAI,KAC9B,KAAI;AACF,SAAM,8BAA8B,IAAI,KAAK;UACvC;AACN,SAAM;;AAIV,MAAI,CAAC,OAAO,IAAI,WAAW,GAAG;AAC5B,eAAY,KAAK,iBAAiB;AAClC;;AAEF,MAAI,IAAI,SAAS,eAAe;AAC9B,eAAY,KAAK,0BAA0B;AAC3C;;AAGF,MAAI;GACF,MAAM,IAAI,MAAM,WAAW,KAAK,WAAW,EACzC,UAAU,UAAU,aAAa,YAAY,OAAO,KAAA,GACrD,CAAC;AACF,eAAY,KAAK,EAAE,KAAK,MAAM,IAAI,8BAA8B;UAC1D;AACN,eAAY,KAAK,eAAe;;;AAKpC,QAAO;EAAE,MADM,CAAC,YAAY,OAAO,QAAQ,CAAC,KAAK,KAAK,EAAE,SAAS,MAAM,CAAC,CAAC,OAAO,QAAQ,CAAC,KAAK,OACzE,IAAI;EAAU,cAAc;EAAM,kBAAkB;EAAa"}
@@ -13,6 +13,7 @@ export interface TelegramTopicConfig {
13
13
  enabled?: boolean;
14
14
  allowFrom?: Array<string | number>;
15
15
  systemPrompt?: string;
16
+ agentId?: string;
16
17
  groupPolicy?: GroupPolicy;
17
18
  }
18
19
  export type DmPolicy = 'pairing' | 'allowlist' | 'open' | 'disabled';
@@ -25,6 +26,7 @@ export interface TelegramGroupConfig {
25
26
  enabled?: boolean;
26
27
  allowFrom?: Array<string | number>;
27
28
  systemPrompt?: string;
29
+ agentId?: string;
28
30
  topics?: Record<string, TelegramTopicConfig>;
29
31
  }
30
32
  export interface TelegramAccountConfig {
@@ -41,9 +43,24 @@ export interface TelegramAccountConfig {
41
43
  groups?: Record<string, TelegramGroupConfig>;
42
44
  historyLimit?: number;
43
45
  textChunkLimit?: number;
44
- streamMode?: 'off' | 'partial' | 'block';
46
+ streaming?: {
47
+ mode?: 'off' | 'partial' | 'block';
48
+ preview?: {
49
+ toolProgress?: boolean;
50
+ };
51
+ block?: {
52
+ coalesce?: {
53
+ minChars?: number;
54
+ idleMs?: number;
55
+ };
56
+ };
57
+ };
45
58
  proxy?: string;
46
59
  apiRoot?: string;
60
+ pollingStallThresholdMs?: number;
61
+ reactionLevel?: 'off' | 'ack' | 'minimal' | 'extensive';
62
+ reactionNotifications?: 'off' | 'own' | 'all';
63
+ ackReaction?: string;
47
64
  }
48
65
  export interface TelegramChannelConfig {
49
66
  enabled?: boolean;
@@ -54,7 +71,18 @@ export interface TelegramChannelConfig {
54
71
  dmPolicy?: DmPolicy;
55
72
  groupPolicy?: GroupPolicy;
56
73
  replyToMode?: ReplyToMode;
57
- streamMode?: 'off' | 'partial' | 'block';
74
+ streaming?: {
75
+ mode?: 'off' | 'partial' | 'block';
76
+ preview?: {
77
+ toolProgress?: boolean;
78
+ };
79
+ block?: {
80
+ coalesce?: {
81
+ minChars?: number;
82
+ idleMs?: number;
83
+ };
84
+ };
85
+ };
58
86
  historyLimit?: number;
59
87
  textChunkLimit?: number;
60
88
  proxy?: string;