@xopcai/xopc 0.0.99 → 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 (349) hide show
  1. package/dist/browser-ext/manifest.json +1 -1
  2. package/dist/extensions/telegram/src/account-manager.d.ts +3 -3
  3. package/dist/extensions/telegram/src/account-manager.js +8 -8
  4. package/dist/extensions/telegram/src/account-manager.js.map +1 -1
  5. package/dist/extensions/telegram/src/actions/message-actions.d.ts +4 -0
  6. package/dist/extensions/telegram/src/actions/message-actions.js +71 -0
  7. package/dist/extensions/telegram/src/actions/message-actions.js.map +1 -0
  8. package/dist/extensions/telegram/src/adapters/config-adapter.js +9 -1
  9. package/dist/extensions/telegram/src/adapters/config-adapter.js.map +1 -1
  10. package/dist/extensions/telegram/src/adapters/config-surface.js +1 -1
  11. package/dist/extensions/telegram/src/adapters/config-surface.js.map +1 -1
  12. package/dist/extensions/telegram/src/adapters/outbound-adapter.d.ts +2 -1
  13. package/dist/extensions/telegram/src/adapters/outbound-adapter.js +18 -4
  14. package/dist/extensions/telegram/src/adapters/outbound-adapter.js.map +1 -1
  15. package/dist/extensions/telegram/src/adapters/security-adapter.js +2 -1
  16. package/dist/extensions/telegram/src/adapters/security-adapter.js.map +1 -1
  17. package/dist/extensions/telegram/src/adapters/streaming-adapter.js +90 -6
  18. package/dist/extensions/telegram/src/adapters/streaming-adapter.js.map +1 -1
  19. package/dist/extensions/telegram/src/adapters/types.d.ts +9 -1
  20. package/dist/extensions/telegram/src/api-root.d.ts +4 -0
  21. package/dist/extensions/telegram/src/api-root.js +42 -0
  22. package/dist/extensions/telegram/src/api-root.js.map +1 -0
  23. package/dist/extensions/telegram/src/approval-delivery.d.ts +9 -0
  24. package/dist/extensions/telegram/src/approval-delivery.js +36 -0
  25. package/dist/extensions/telegram/src/approval-delivery.js.map +1 -0
  26. package/dist/extensions/telegram/src/approval-store.d.ts +25 -0
  27. package/dist/extensions/telegram/src/approval-store.js +61 -0
  28. package/dist/extensions/telegram/src/approval-store.js.map +1 -0
  29. package/dist/extensions/telegram/src/config-schema.d.ts +185 -35
  30. package/dist/extensions/telegram/src/config-schema.js +67 -45
  31. package/dist/extensions/telegram/src/config-schema.js.map +1 -1
  32. package/dist/extensions/telegram/src/conversation-id.d.ts +6 -0
  33. package/dist/extensions/telegram/src/conversation-id.js +18 -0
  34. package/dist/extensions/telegram/src/conversation-id.js.map +1 -0
  35. package/dist/extensions/telegram/src/doctor.d.ts +5 -0
  36. package/dist/extensions/telegram/src/doctor.js +80 -0
  37. package/dist/extensions/telegram/src/doctor.js.map +1 -0
  38. package/dist/extensions/telegram/src/exec-approval-handler.d.ts +7 -0
  39. package/dist/extensions/telegram/src/exec-approval-handler.js +36 -0
  40. package/dist/extensions/telegram/src/exec-approval-handler.js.map +1 -0
  41. package/dist/extensions/telegram/src/exec-approvals.d.ts +8 -0
  42. package/dist/extensions/telegram/src/exec-approvals.js +20 -0
  43. package/dist/extensions/telegram/src/exec-approvals.js.map +1 -0
  44. package/dist/extensions/telegram/src/focus-handler.d.ts +12 -0
  45. package/dist/extensions/telegram/src/focus-handler.js +55 -0
  46. package/dist/extensions/telegram/src/focus-handler.js.map +1 -0
  47. package/dist/extensions/telegram/src/group-config-resolver.d.ts +12 -0
  48. package/dist/extensions/telegram/src/group-config-resolver.js +17 -0
  49. package/dist/extensions/telegram/src/group-config-resolver.js.map +1 -0
  50. package/dist/extensions/telegram/src/inbound-coalescer.d.ts +15 -0
  51. package/dist/extensions/telegram/src/inbound-coalescer.js +107 -0
  52. package/dist/extensions/telegram/src/inbound-coalescer.js.map +1 -0
  53. package/dist/extensions/telegram/src/inbound-processor.d.ts +1 -0
  54. package/dist/extensions/telegram/src/inbound-processor.js +41 -4
  55. package/dist/extensions/telegram/src/inbound-processor.js.map +1 -1
  56. package/dist/extensions/telegram/src/network-errors.d.ts +2 -0
  57. package/dist/extensions/telegram/src/network-errors.js +23 -0
  58. package/dist/extensions/telegram/src/network-errors.js.map +1 -0
  59. package/dist/extensions/telegram/src/plugin.d.ts +11 -0
  60. package/dist/extensions/telegram/src/plugin.js +191 -57
  61. package/dist/extensions/telegram/src/plugin.js.map +1 -1
  62. package/dist/extensions/telegram/src/polling-session.d.ts +17 -0
  63. package/dist/extensions/telegram/src/polling-session.js +112 -0
  64. package/dist/extensions/telegram/src/polling-session.js.map +1 -0
  65. package/dist/extensions/telegram/src/proxy-fetch.d.ts +2 -0
  66. package/dist/extensions/telegram/src/proxy-fetch.js +14 -0
  67. package/dist/extensions/telegram/src/proxy-fetch.js.map +1 -0
  68. package/dist/extensions/telegram/src/reactions.d.ts +14 -0
  69. package/dist/extensions/telegram/src/reactions.js +48 -0
  70. package/dist/extensions/telegram/src/reactions.js.map +1 -0
  71. package/dist/extensions/telegram/src/reply-params.d.ts +15 -0
  72. package/dist/extensions/telegram/src/reply-params.js +28 -0
  73. package/dist/extensions/telegram/src/reply-params.js.map +1 -0
  74. package/dist/extensions/telegram/src/startup-errors.d.ts +3 -0
  75. package/dist/extensions/telegram/src/startup-errors.js +27 -0
  76. package/dist/extensions/telegram/src/startup-errors.js.map +1 -0
  77. package/dist/extensions/telegram/src/telegram-actions.d.ts +25 -0
  78. package/dist/extensions/telegram/src/telegram-actions.js +65 -0
  79. package/dist/extensions/telegram/src/telegram-actions.js.map +1 -0
  80. package/dist/extensions/telegram/src/thread-bindings.d.ts +15 -0
  81. package/dist/extensions/telegram/src/thread-bindings.js +22 -0
  82. package/dist/extensions/telegram/src/thread-bindings.js.map +1 -0
  83. package/dist/extensions/telegram/src/token-resolver.d.ts +8 -0
  84. package/dist/extensions/telegram/src/token-resolver.js +34 -0
  85. package/dist/extensions/telegram/src/token-resolver.js.map +1 -0
  86. package/dist/extensions/telegram/src/update-offset-store.d.ts +9 -0
  87. package/dist/extensions/telegram/src/update-offset-store.js +66 -0
  88. package/dist/extensions/telegram/src/update-offset-store.js.map +1 -0
  89. package/dist/extensions/telegram/xopc.extension.json +1 -1
  90. package/dist/gateway/static/root/assets/{agents-4KTK-Ku7.js → agents-C5jtBK-H.js} +1 -1
  91. package/dist/gateway/static/root/assets/{apps-page-DkgInY2w.js → apps-page-smJl-qf_.js} +1 -1
  92. package/dist/gateway/static/root/assets/channels-settings-CH6KgFK6.js +1 -0
  93. package/dist/gateway/static/root/assets/channels-status-swr-CV70xvY7.js +8 -0
  94. package/dist/gateway/static/root/assets/{cron-api-BRDli2Mm.js → cron-api-CgBO8B7I.js} +1 -1
  95. package/dist/gateway/static/root/assets/{cron-page-BzEr5Za8.js → cron-page-Be0cW1cR.js} +1 -1
  96. package/dist/gateway/static/root/assets/{dist-BsHh1vX0.js → dist-P8HRGUuh.js} +1 -1
  97. package/dist/gateway/static/root/assets/{extension-debug-page-TWt5q_ef.js → extension-debug-page-BowuycYu.js} +1 -1
  98. package/dist/gateway/static/root/assets/{extension-page-C3kV8ZvZ.js → extension-page-27-Zpv8O.js} +1 -1
  99. package/dist/gateway/static/root/assets/{extension-settings-page-Csy9LNC6.js → extension-settings-page-DDFa_rIq.js} +1 -1
  100. package/dist/gateway/static/root/assets/{fetch-BLLOP2CM.js → fetch-drJUxxKP.js} +1 -1
  101. package/dist/gateway/static/root/assets/{field-primitives-MXbM8HvY.js → field-primitives-BWLssxEb.js} +1 -1
  102. package/dist/gateway/static/root/assets/{heartbeat-config-api-CkoyqfRx.js → heartbeat-config-api-BVuYNYT_.js} +1 -1
  103. package/dist/gateway/static/root/assets/index-CRDAmr3W.js +4840 -0
  104. package/dist/gateway/static/root/assets/index-DjNDHzwG.css +1 -0
  105. package/dist/gateway/static/root/assets/{logs-page-B2vY41ph.js → logs-page-BDX8IrDQ.js} +1 -1
  106. package/dist/gateway/static/root/assets/{note-detail-page-B7qE5pkO.js → note-detail-page-BmQ9KeiA.js} +1 -1
  107. package/dist/gateway/static/root/assets/{note-time-NBBReVR4.js → note-time-14I9Sgqh.js} +1 -1
  108. package/dist/gateway/static/root/assets/{notes-page-CsWYaqYM.js → notes-page-BMfP0FFf.js} +1 -1
  109. package/dist/gateway/static/root/assets/{sessions-page-BPGY3SCT.js → sessions-page-C3scyChq.js} +1 -1
  110. package/dist/gateway/static/root/assets/{settings-advanced-gate-DtA451RM.js → settings-advanced-gate-2ODuCjHP.js} +1 -1
  111. package/dist/gateway/static/root/assets/{settings-form-section-DYR_SPlX.js → settings-form-section-DUoGKAiW.js} +1 -1
  112. package/dist/gateway/static/root/assets/{settings-page-BJUgZQzN.js → settings-page-CttAfOWV.js} +1 -1
  113. package/dist/gateway/static/root/assets/{share-preview-page-3u8VFmdo.js → share-preview-page-B77xqiiI.js} +1 -1
  114. package/dist/gateway/static/root/assets/{skills-page-DuyhcH4U.js → skills-page-B_bAZGzB.js} +1 -1
  115. package/dist/gateway/static/root/assets/{theme-store-e2q2yjs4.js → theme-store-BgCPrmxU.js} +1 -1
  116. package/dist/gateway/static/root/assets/url-q28QhI7l.js +3 -0
  117. package/dist/gateway/static/root/assets/{utils-B4Vw3fnc.js → utils-CCGejlcX.js} +1 -1
  118. package/dist/gateway/static/root/assets/{voice-api-key-field-BFY67Hhp.js → voice-api-key-field-DZzzihcY.js} +1 -1
  119. package/dist/gateway/static/root/assets/{workflow-page.utils-yUJ5jexd.js → workflow-page.utils-D5RMAmT4.js} +1 -1
  120. package/dist/gateway/static/root/assets/{workflows-page-DIMHCpNt.js → workflows-page-DEYpC6Bv.js} +1 -1
  121. package/dist/gateway/static/root/index.html +5 -5
  122. package/dist/package.js +1 -1
  123. package/dist/src/agent/agent-manager.d.ts +4 -0
  124. package/dist/src/agent/agent-manager.js +68 -4
  125. package/dist/src/agent/agent-manager.js.map +1 -1
  126. package/dist/src/agent/child-agent-factory.d.ts +1 -0
  127. package/dist/src/agent/child-agent-factory.js +13 -10
  128. package/dist/src/agent/child-agent-factory.js.map +1 -1
  129. package/dist/src/agent/embedded/run-for-session.d.ts +1 -0
  130. package/dist/src/agent/embedded/run-for-session.js +1 -0
  131. package/dist/src/agent/embedded/run-for-session.js.map +1 -1
  132. package/dist/src/agent/embedded/run-turn.js +1 -17
  133. package/dist/src/agent/embedded/run-turn.js.map +1 -1
  134. package/dist/src/agent/embedded/session-runner.js +3 -1
  135. package/dist/src/agent/embedded/session-runner.js.map +1 -1
  136. package/dist/src/agent/embedded/session-tool-result-guard.d.ts +1 -0
  137. package/dist/src/agent/embedded/session-tool-result-guard.js +1 -0
  138. package/dist/src/agent/embedded/session-tool-result-guard.js.map +1 -1
  139. package/dist/src/agent/inbound/attachment-pipeline.d.ts +37 -0
  140. package/dist/src/agent/inbound/attachment-pipeline.js +169 -0
  141. package/dist/src/agent/inbound/attachment-pipeline.js.map +1 -0
  142. package/dist/src/agent/inbound/turn-dispatcher.d.ts +3 -5
  143. package/dist/src/agent/inbound/turn-dispatcher.js +1 -2
  144. package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
  145. package/dist/src/agent/memory/index.d.ts +2 -0
  146. package/dist/src/agent/memory/index.js +2 -1
  147. package/dist/src/agent/memory/memory-flush.d.ts +27 -0
  148. package/dist/src/agent/memory/memory-flush.js +124 -0
  149. package/dist/src/agent/memory/memory-flush.js.map +1 -0
  150. package/dist/src/agent/messaging/outbound-coordinator.js +2 -1
  151. package/dist/src/agent/messaging/outbound-coordinator.js.map +1 -1
  152. package/dist/src/agent/messaging/stream-manager.d.ts +2 -0
  153. package/dist/src/agent/messaging/stream-manager.js +3 -0
  154. package/dist/src/agent/messaging/stream-manager.js.map +1 -1
  155. package/dist/src/agent/orchestration/agent-orchestrator.d.ts +0 -4
  156. package/dist/src/agent/orchestration/agent-orchestrator.js +40 -107
  157. package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
  158. package/dist/src/agent/prompt/contribution.d.ts +9 -0
  159. package/dist/src/agent/prompt/contribution.js +1 -0
  160. package/dist/src/agent/prompt/safety.js +0 -10
  161. package/dist/src/agent/prompt/safety.js.map +1 -1
  162. package/dist/src/agent/prompt/sanitize-for-prompt.d.ts +6 -0
  163. package/dist/src/agent/prompt/sanitize-for-prompt.js +13 -0
  164. package/dist/src/agent/prompt/sanitize-for-prompt.js.map +1 -0
  165. package/dist/src/agent/prompt/sections/behavior.d.ts +5 -0
  166. package/dist/src/agent/prompt/sections/behavior.js +50 -0
  167. package/dist/src/agent/prompt/sections/behavior.js.map +1 -0
  168. package/dist/src/agent/prompt/sections/memory-skills.d.ts +9 -0
  169. package/dist/src/agent/prompt/sections/memory-skills.js +59 -0
  170. package/dist/src/agent/prompt/sections/memory-skills.js.map +1 -0
  171. package/dist/src/agent/prompt/sections/messaging-runtime.d.ts +16 -0
  172. package/dist/src/agent/prompt/sections/messaging-runtime.js +69 -0
  173. package/dist/src/agent/prompt/sections/messaging-runtime.js.map +1 -0
  174. package/dist/src/agent/prompt/sections/project-context.d.ts +16 -0
  175. package/dist/src/agent/prompt/sections/project-context.js +58 -0
  176. package/dist/src/agent/prompt/sections/project-context.js.map +1 -0
  177. package/dist/src/agent/prompt/sections/tooling.d.ts +5 -0
  178. package/dist/src/agent/prompt/sections/tooling.js +119 -0
  179. package/dist/src/agent/prompt/sections/tooling.js.map +1 -0
  180. package/dist/src/agent/prompt/sections/workspace-runtime.d.ts +11 -0
  181. package/dist/src/agent/prompt/sections/workspace-runtime.js +32 -0
  182. package/dist/src/agent/prompt/sections/workspace-runtime.js.map +1 -0
  183. package/dist/src/agent/prompt/service-prompt-builder.d.ts +13 -0
  184. package/dist/src/agent/prompt/service-prompt-builder.js +54 -13
  185. package/dist/src/agent/prompt/service-prompt-builder.js.map +1 -1
  186. package/dist/src/agent/prompt/subagent-context.d.ts +15 -0
  187. package/dist/src/agent/prompt/subagent-context.js +68 -0
  188. package/dist/src/agent/prompt/subagent-context.js.map +1 -0
  189. package/dist/src/agent/prompt/system-prompt-params.d.ts +53 -0
  190. package/dist/src/agent/prompt/system-prompt-params.js +74 -0
  191. package/dist/src/agent/prompt/system-prompt-params.js.map +1 -0
  192. package/dist/src/agent/prompt/system-prompt.d.ts +22 -11
  193. package/dist/src/agent/prompt/system-prompt.js +79 -193
  194. package/dist/src/agent/prompt/system-prompt.js.map +1 -1
  195. package/dist/src/agent/prompt/types.d.ts +3 -0
  196. package/dist/src/agent/service/build-direct-message-content.d.ts +5 -21
  197. package/dist/src/agent/service/build-direct-message-content.js +11 -64
  198. package/dist/src/agent/service/build-direct-message-content.js.map +1 -1
  199. package/dist/src/agent/service/direct-turn-helpers.d.ts +1 -1
  200. package/dist/src/agent/service/direct-turn-helpers.js +7 -0
  201. package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
  202. package/dist/src/agent/service/process-direct-one-shot.d.ts +3 -3
  203. package/dist/src/agent/service/process-direct-one-shot.js +22 -17
  204. package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
  205. package/dist/src/agent/service/process-direct-streaming.d.ts +6 -20
  206. package/dist/src/agent/service/process-direct-streaming.js +39 -40
  207. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  208. package/dist/src/agent/service/webchat-tts.d.ts +4 -9
  209. package/dist/src/agent/service/webchat-tts.js +9 -20
  210. package/dist/src/agent/service/webchat-tts.js.map +1 -1
  211. package/dist/src/agent/service.d.ts +8 -19
  212. package/dist/src/agent/service.js +53 -11
  213. package/dist/src/agent/service.js.map +1 -1
  214. package/dist/src/agent/tools/factory.js +2 -0
  215. package/dist/src/agent/tools/factory.js.map +1 -1
  216. package/dist/src/agent/tools/index.d.ts +1 -0
  217. package/dist/src/agent/tools/index.js +2 -1
  218. package/dist/src/agent/tools/media-read-tool.d.ts +2 -0
  219. package/dist/src/agent/tools/media-read-tool.js +89 -0
  220. package/dist/src/agent/tools/media-read-tool.js.map +1 -0
  221. package/dist/src/channels/attachments/attachment-classifiers.d.ts +8 -0
  222. package/dist/src/channels/attachments/attachment-classifiers.js +11 -0
  223. package/dist/src/channels/attachments/attachment-classifiers.js.map +1 -0
  224. package/dist/src/channels/attachments/inbound-persist.d.ts +13 -34
  225. package/dist/src/channels/attachments/inbound-persist.js +34 -102
  226. package/dist/src/channels/attachments/inbound-persist.js.map +1 -1
  227. package/dist/src/channels/attachments/inbound-types.d.ts +13 -0
  228. package/dist/src/channels/attachments/inbound-types.js +30 -0
  229. package/dist/src/channels/attachments/inbound-types.js.map +1 -0
  230. package/dist/src/channels/attachments/outbound-tts-persist.d.ts +3 -12
  231. package/dist/src/channels/attachments/outbound-tts-persist.js +28 -48
  232. package/dist/src/channels/attachments/outbound-tts-persist.js.map +1 -1
  233. package/dist/src/channels/attachments/voice-stt-webchat.d.ts +3 -3
  234. package/dist/src/channels/attachments/voice-stt-webchat.js +8 -14
  235. package/dist/src/channels/attachments/voice-stt-webchat.js.map +1 -1
  236. package/dist/src/channels/channel-domain.d.ts +30 -2
  237. package/dist/src/channels/exec-approval-runtime.d.ts +29 -0
  238. package/dist/src/channels/exec-approval-runtime.js +30 -0
  239. package/dist/src/channels/exec-approval-runtime.js.map +1 -0
  240. package/dist/src/channels/index.js +1 -1
  241. package/dist/src/channels/plugin-types.d.ts +1 -0
  242. package/dist/src/channels/transport-types.d.ts +2 -2
  243. package/dist/src/cli/commands/channels.js +1 -1
  244. package/dist/src/cli/commands/doctor/checks/database-schema.d.ts +2 -0
  245. package/dist/src/cli/commands/doctor/checks/database-schema.js +62 -0
  246. package/dist/src/cli/commands/doctor/checks/database-schema.js.map +1 -0
  247. package/dist/src/cli/commands/doctor/flow.js +2 -0
  248. package/dist/src/cli/commands/doctor/flow.js.map +1 -1
  249. package/dist/src/cli/commands/init.js +2 -57
  250. package/dist/src/cli/commands/init.js.map +1 -1
  251. package/dist/src/cli/commands/onboard/channels/registry.js +1 -1
  252. package/dist/src/cli/commands/setup.js +6 -5
  253. package/dist/src/cli/commands/setup.js.map +1 -1
  254. package/dist/src/cli/utils/init-workspace-core.js +1 -2
  255. package/dist/src/cli/utils/init-workspace-core.js.map +1 -1
  256. package/dist/src/config/index.js +2 -2
  257. package/dist/src/config/paths.d.ts +0 -13
  258. package/dist/src/config/paths.js +1 -20
  259. package/dist/src/config/paths.js.map +1 -1
  260. package/dist/src/config/rules.js +1 -1
  261. package/dist/src/config/schema.d.ts +24 -0
  262. package/dist/src/config/schema.js +33 -2
  263. package/dist/src/config/schema.js.map +1 -1
  264. package/dist/src/cron/executor.js +2 -2
  265. package/dist/src/gateway/heartbeat/service.js +1 -1
  266. package/dist/src/gateway/hono/lib/config-payload.js +1 -1
  267. package/dist/src/gateway/hono/routes/config-patch/channels.js +3 -3
  268. package/dist/src/gateway/hono/routes/config-patch/channels.js.map +1 -1
  269. package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
  270. package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
  271. package/dist/src/gateway/hono/routes/media.d.ts +3 -0
  272. package/dist/src/gateway/hono/routes/media.js +50 -0
  273. package/dist/src/gateway/hono/routes/media.js.map +1 -0
  274. package/dist/src/gateway/hono/routes/sessions.js +6 -0
  275. package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
  276. package/dist/src/gateway/hono/routes/workspace.js +1 -87
  277. package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
  278. package/dist/src/gateway/service/sessions-api.js +3 -0
  279. package/dist/src/gateway/service/sessions-api.js.map +1 -1
  280. package/dist/src/heartbeat/index.js +1 -1
  281. package/dist/src/media/index.d.ts +5 -0
  282. package/dist/src/media/index.js +5 -0
  283. package/dist/src/media/media-reference.d.ts +23 -0
  284. package/dist/src/media/media-reference.js +34 -0
  285. package/dist/src/media/media-reference.js.map +1 -0
  286. package/dist/src/media/paths.d.ts +5 -0
  287. package/dist/src/media/paths.js +20 -0
  288. package/dist/src/media/paths.js.map +1 -0
  289. package/dist/src/media/session-references.d.ts +8 -0
  290. package/dist/src/media/session-references.js +45 -0
  291. package/dist/src/media/session-references.js.map +1 -0
  292. package/dist/src/media/store.d.ts +19 -0
  293. package/dist/src/media/store.js +131 -0
  294. package/dist/src/media/store.js.map +1 -0
  295. package/dist/src/media/types.d.ts +34 -0
  296. package/dist/src/media/types.js +1 -0
  297. package/dist/src/media/uri.d.ts +15 -0
  298. package/dist/src/media/uri.js +51 -0
  299. package/dist/src/media/uri.js.map +1 -0
  300. package/dist/src/session/session-title.js +6 -3
  301. package/dist/src/session/session-title.js.map +1 -1
  302. package/dist/src/session/store.js +1 -1
  303. package/dist/src/session/store.js.map +1 -1
  304. package/dist/src/session/types.d.ts +12 -7
  305. package/dist/src/session/types.js.map +1 -1
  306. package/dist/src/storage/sqlite/index.d.ts +4 -1
  307. package/dist/src/storage/sqlite/index.js +6 -2
  308. package/dist/src/storage/sqlite/migrations/002_add_flush_columns.sql +3 -0
  309. package/dist/src/storage/sqlite/migrations/discover.d.ts +4 -0
  310. package/dist/src/storage/sqlite/migrations/discover.js +38 -0
  311. package/dist/src/storage/sqlite/migrations/discover.js.map +1 -0
  312. package/dist/src/storage/sqlite/migrations/discover.ts +52 -0
  313. package/dist/src/storage/sqlite/migrations/errors.d.ts +11 -0
  314. package/dist/src/storage/sqlite/migrations/errors.js +27 -0
  315. package/dist/src/storage/sqlite/migrations/errors.js.map +1 -0
  316. package/dist/src/storage/sqlite/migrations/errors.ts +32 -0
  317. package/dist/src/storage/sqlite/migrations/runner.d.ts +22 -0
  318. package/dist/src/storage/sqlite/migrations/runner.js +97 -0
  319. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -0
  320. package/dist/src/storage/sqlite/migrations/runner.ts +153 -0
  321. package/dist/src/storage/sqlite/migrations/types.d.ts +12 -0
  322. package/dist/src/storage/sqlite/migrations/types.js +1 -0
  323. package/dist/src/storage/sqlite/migrations/types.ts +13 -0
  324. package/dist/src/storage/sqlite/row-mappers.d.ts +4 -0
  325. package/dist/src/storage/sqlite/row-mappers.js +4 -0
  326. package/dist/src/storage/sqlite/row-mappers.js.map +1 -1
  327. package/dist/src/storage/sqlite/schema-version.d.ts +6 -0
  328. package/dist/src/storage/sqlite/schema-version.js +29 -0
  329. package/dist/src/storage/sqlite/schema-version.js.map +1 -0
  330. package/dist/src/storage/sqlite/schema.d.ts +5 -3
  331. package/dist/src/storage/sqlite/schema.js +24 -28
  332. package/dist/src/storage/sqlite/schema.js.map +1 -1
  333. package/dist/src/storage/sqlite/schema.sql +2 -0
  334. package/dist/src/storage/sqlite/session-metadata.js +1 -0
  335. package/dist/src/storage/sqlite/session-metadata.js.map +1 -1
  336. package/dist/src/storage/sqlite/session-repository.js +7 -3
  337. package/dist/src/storage/sqlite/session-repository.js.map +1 -1
  338. package/package.json +1 -1
  339. package/dist/gateway/static/root/assets/channels-settings-BxW1Xio4.js +0 -1
  340. package/dist/gateway/static/root/assets/channels-status-swr-Cxm6AvQO.js +0 -8
  341. package/dist/gateway/static/root/assets/index-BJDmBCSl.css +0 -1
  342. package/dist/gateway/static/root/assets/index-Cbg5TS5m.js +0 -4707
  343. package/dist/gateway/static/root/assets/url-DpFBIyN9.js +0 -3
  344. package/dist/src/cli/commands/onboard/workspace.d.ts +0 -14
  345. package/dist/src/cli/commands/onboard/workspace.js +0 -33
  346. package/dist/src/cli/commands/onboard/workspace.js.map +0 -1
  347. package/dist/src/cli/utils/workspace.d.ts +0 -35
  348. package/dist/src/cli/utils/workspace.js +0 -72
  349. package/dist/src/cli/utils/workspace.js.map +0 -1
@@ -12,6 +12,7 @@ export { createWebSearchTool, createWebFetchTool } from './web.js';
12
12
  export { createWebExtractTool, stripHtmlBoilerplate, DEFAULT_WEB_EXTRACT_MAX_LENGTH, MAX_RAW_HTML_CHARS_FOR_WEB_EXTRACT, } from './web-extract.js';
13
13
  export { createMessageTool } from './communication.js';
14
14
  export { createSendMediaTool } from './send-media.js';
15
+ export { createReadMediaTool } from './media-read-tool.js';
15
16
  export { createCreateShareTool, isShareToolAvailable, type CreateShareToolDeps, } from './create-share-tool.js';
16
17
  export { createTodoTool, TodoStore, type TodoItem, type TodoStatus } from './todo-tool.js';
17
18
  export { createSessionStatusTool } from './session-status-tool.js';
@@ -14,6 +14,7 @@ import { createWebFetchTool, createWebSearchTool } from "./web.js";
14
14
  import { DEFAULT_WEB_EXTRACT_MAX_LENGTH, MAX_RAW_HTML_CHARS_FOR_WEB_EXTRACT, createWebExtractTool, stripHtmlBoilerplate } from "./web-extract.js";
15
15
  import { createMessageTool } from "./communication.js";
16
16
  import { createSendMediaTool } from "./send-media.js";
17
+ import { createReadMediaTool } from "./media-read-tool.js";
17
18
  import { createCreateShareTool, isShareToolAvailable } from "./create-share-tool.js";
18
19
  import { TodoStore, createTodoTool } from "./todo-tool.js";
19
20
  import { createSessionStatusTool } from "./session-status-tool.js";
@@ -31,4 +32,4 @@ import { createSkillManageTool } from "./skill-manage-tool.js";
31
32
  import { resolveImageModelConfigForTool } from "../image/tool-model-config.js";
32
33
  import { createImageTool } from "./image-tool.js";
33
34
  import { createImageGenerateTool, resolveImageGenerationModelConfigForTool } from "./image-generate-tool.js";
34
- export { BrowserManager, DEFAULT_DELEGATE_TOOLS, DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, DEFAULT_WEB_EXTRACT_MAX_LENGTH, DELEGATE_BLOCKED_TOOLS, GREP_MAX_LINE_LENGTH, MAX_RAW_HTML_CHARS_FOR_WEB_EXTRACT, SANDBOX_ALLOWED_TOOLS, TodoStore, assertBrowserUrlAllowed, buildSandboxToolMap, createClarifyTool, createCreateShareTool, createCronjobTool, createCuratedMemoryTool, createDelegateTool, createDreamingTool, createEditFileTool, createExecuteCodeTool, createFindTool, createGrepTool, createImageGenerateTool, createImageTool, createListDirTool, createMemoryGetTool, createMemorySearchTool, createMessageTool, createReadFileTool, createSendMediaTool, createSessionSearchTool, createSessionStatusTool, createShellTool, createSkillManageTool, createSkillViewTool, createSkillsListTool, createTodoTool, createWebExtractTool, createWebFetchTool, createWebSearchTool, createWorkflowTool, createWriteFileTool, editFileTool, findTool, formatSize, fuzzyFindText, generateDiffString, grepTool, isShareToolAvailable, listDirTool, normalizeForFuzzyMatch, normalizeToLF, resolveImageGenerationModelConfigForTool, resolveImageModelConfigForTool, restoreLineEndings, scanCronPrompt, stripBom, stripHtmlBoilerplate, truncateHead, truncateLine, truncateTail, writeFileTool };
35
+ export { BrowserManager, DEFAULT_DELEGATE_TOOLS, DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, DEFAULT_WEB_EXTRACT_MAX_LENGTH, DELEGATE_BLOCKED_TOOLS, GREP_MAX_LINE_LENGTH, MAX_RAW_HTML_CHARS_FOR_WEB_EXTRACT, SANDBOX_ALLOWED_TOOLS, TodoStore, assertBrowserUrlAllowed, buildSandboxToolMap, createClarifyTool, createCreateShareTool, createCronjobTool, createCuratedMemoryTool, createDelegateTool, createDreamingTool, createEditFileTool, createExecuteCodeTool, createFindTool, createGrepTool, createImageGenerateTool, createImageTool, createListDirTool, createMemoryGetTool, createMemorySearchTool, createMessageTool, createReadFileTool, createReadMediaTool, createSendMediaTool, createSessionSearchTool, createSessionStatusTool, createShellTool, createSkillManageTool, createSkillViewTool, createSkillsListTool, createTodoTool, createWebExtractTool, createWebFetchTool, createWebSearchTool, createWorkflowTool, createWriteFileTool, editFileTool, findTool, formatSize, fuzzyFindText, generateDiffString, grepTool, isShareToolAvailable, listDirTool, normalizeForFuzzyMatch, normalizeToLF, resolveImageGenerationModelConfigForTool, resolveImageModelConfigForTool, restoreLineEndings, scanCronPrompt, stripBom, stripHtmlBoilerplate, truncateHead, truncateLine, truncateTail, writeFileTool };
@@ -0,0 +1,2 @@
1
+ import type { AgentTool } from '@earendil-works/pi-agent-core';
2
+ export declare function createReadMediaTool(): AgentTool;
@@ -0,0 +1,89 @@
1
+ import { mimeTypeFromMediaPath } from "../../media/store.js";
2
+ import { readMediaReference } from "../../media/media-reference.js";
3
+ import { Type } from "@sinclair/typebox";
4
+ //#region src/agent/tools/media-read-tool.ts
5
+ const DEFAULT_MAX_BYTES = 1024 * 1024;
6
+ const HARD_MAX_BYTES = 5 * 1024 * 1024;
7
+ const ReadMediaSchema = Type.Object({
8
+ uri: Type.String({ description: "The media:// URI from an xopc-media-uri line in the user message." }),
9
+ maxBytes: Type.Optional(Type.Number({ description: "Maximum bytes to read, capped at 5 MiB." }))
10
+ });
11
+ function isLikelyText(mimeType) {
12
+ return mimeType.startsWith("text/") || mimeType === "application/json" || mimeType === "application/xml" || mimeType === "application/javascript" || mimeType === "application/typescript";
13
+ }
14
+ function clampMaxBytes(raw) {
15
+ if (typeof raw !== "number" || !Number.isFinite(raw) || raw <= 0) return DEFAULT_MAX_BYTES;
16
+ return Math.min(Math.floor(raw), HARD_MAX_BYTES);
17
+ }
18
+ function createReadMediaTool() {
19
+ return {
20
+ name: "read_media",
21
+ label: "Read Media Attachment",
22
+ description: "Read an attachment by media:// URI. Use this for xopc-media-uri attachments in user messages.",
23
+ parameters: ReadMediaSchema,
24
+ async execute(_toolCallId, params, _signal) {
25
+ const uri = typeof params.uri === "string" ? params.uri.trim() : "";
26
+ if (!uri) return {
27
+ content: [{
28
+ type: "text",
29
+ text: "Missing media URI."
30
+ }],
31
+ details: {
32
+ ok: false,
33
+ error: "missing_uri"
34
+ }
35
+ };
36
+ try {
37
+ const { buffer, path } = await readMediaReference(uri, clampMaxBytes(params.maxBytes));
38
+ const mimeType = mimeTypeFromMediaPath(path);
39
+ const metadata = {
40
+ ok: true,
41
+ uri,
42
+ mimeType,
43
+ size: buffer.byteLength,
44
+ path
45
+ };
46
+ if (isLikelyText(mimeType)) return {
47
+ content: [{
48
+ type: "text",
49
+ text: buffer.toString("utf8")
50
+ }],
51
+ details: {
52
+ ...metadata,
53
+ kind: "text"
54
+ }
55
+ };
56
+ return {
57
+ content: [{
58
+ type: "text",
59
+ text: JSON.stringify({
60
+ ...metadata,
61
+ kind: "binary",
62
+ base64: buffer.toString("base64")
63
+ }, null, 2)
64
+ }],
65
+ details: {
66
+ ...metadata,
67
+ kind: "binary"
68
+ }
69
+ };
70
+ } catch (err) {
71
+ const message = err instanceof Error ? err.message : String(err);
72
+ return {
73
+ content: [{
74
+ type: "text",
75
+ text: `Media read error: ${message}`
76
+ }],
77
+ details: {
78
+ ok: false,
79
+ error: message
80
+ }
81
+ };
82
+ }
83
+ }
84
+ };
85
+ }
86
+ //#endregion
87
+ export { createReadMediaTool };
88
+
89
+ //# sourceMappingURL=media-read-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-read-tool.js","names":[],"sources":["../../../../src/agent/tools/media-read-tool.ts"],"sourcesContent":["import { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\n\nimport { readMediaReference } from '../../media/media-reference.js';\nimport { mimeTypeFromMediaPath } from '../../media/store.js';\n\nconst DEFAULT_MAX_BYTES = 1024 * 1024;\nconst HARD_MAX_BYTES = 5 * 1024 * 1024;\n\nconst ReadMediaSchema = Type.Object({\n uri: Type.String({\n description: 'The media:// URI from an xopc-media-uri line in the user message.',\n }),\n maxBytes: Type.Optional(Type.Number({\n description: 'Maximum bytes to read, capped at 5 MiB.',\n })),\n});\n\nfunction isLikelyText(mimeType: string): boolean {\n return (\n mimeType.startsWith('text/') ||\n mimeType === 'application/json' ||\n mimeType === 'application/xml' ||\n mimeType === 'application/javascript' ||\n mimeType === 'application/typescript'\n );\n}\n\nfunction clampMaxBytes(raw: unknown): number {\n if (typeof raw !== 'number' || !Number.isFinite(raw) || raw <= 0) {\n return DEFAULT_MAX_BYTES;\n }\n return Math.min(Math.floor(raw), HARD_MAX_BYTES);\n}\n\nexport function createReadMediaTool(): AgentTool {\n return {\n name: 'read_media',\n label: 'Read Media Attachment',\n description:\n 'Read an attachment by media:// URI. Use this for xopc-media-uri attachments in user messages.',\n parameters: ReadMediaSchema,\n\n async execute(\n _toolCallId: string,\n params: { uri?: string; maxBytes?: number },\n _signal?: AbortSignal,\n ): Promise<AgentToolResult<Record<string, unknown>>> {\n const uri = typeof params.uri === 'string' ? params.uri.trim() : '';\n if (!uri) {\n return {\n content: [{ type: 'text', text: 'Missing media URI.' }],\n details: { ok: false, error: 'missing_uri' },\n };\n }\n\n try {\n const maxBytes = clampMaxBytes(params.maxBytes);\n const { buffer, path } = await readMediaReference(uri, maxBytes);\n const mimeType = mimeTypeFromMediaPath(path);\n const metadata = {\n ok: true,\n uri,\n mimeType,\n size: buffer.byteLength,\n path,\n };\n\n if (isLikelyText(mimeType)) {\n const text = buffer.toString('utf8');\n return {\n content: [{ type: 'text', text }],\n details: { ...metadata, kind: 'text' },\n };\n }\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify({\n ...metadata,\n kind: 'binary',\n base64: buffer.toString('base64'),\n }, null, 2),\n },\n ],\n details: { ...metadata, kind: 'binary' },\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n content: [{ type: 'text', text: `Media read error: ${message}` }],\n details: { ok: false, error: message },\n };\n }\n },\n } as AgentTool;\n}\n"],"mappings":";;;;AAMA,MAAM,oBAAoB,OAAO;AACjC,MAAM,iBAAiB,IAAI,OAAO;AAElC,MAAM,kBAAkB,KAAK,OAAO;CAClC,KAAK,KAAK,OAAO,EACf,aAAa,qEACd,CAAC;CACF,UAAU,KAAK,SAAS,KAAK,OAAO,EAClC,aAAa,2CACd,CAAC,CAAC;CACJ,CAAC;AAEF,SAAS,aAAa,UAA2B;AAC/C,QACE,SAAS,WAAW,QAAQ,IAC5B,aAAa,sBACb,aAAa,qBACb,aAAa,4BACb,aAAa;;AAIjB,SAAS,cAAc,KAAsB;AAC3C,KAAI,OAAO,QAAQ,YAAY,CAAC,OAAO,SAAS,IAAI,IAAI,OAAO,EAC7D,QAAO;AAET,QAAO,KAAK,IAAI,KAAK,MAAM,IAAI,EAAE,eAAe;;AAGlD,SAAgB,sBAAiC;AAC/C,QAAO;EACL,MAAM;EACN,OAAO;EACP,aACE;EACF,YAAY;EAEZ,MAAM,QACJ,aACA,QACA,SACmD;GACnD,MAAM,MAAM,OAAO,OAAO,QAAQ,WAAW,OAAO,IAAI,MAAM,GAAG;AACjE,OAAI,CAAC,IACH,QAAO;IACL,SAAS,CAAC;KAAE,MAAM;KAAQ,MAAM;KAAsB,CAAC;IACvD,SAAS;KAAE,IAAI;KAAO,OAAO;KAAe;IAC7C;AAGH,OAAI;IAEF,MAAM,EAAE,QAAQ,SAAS,MAAM,mBAAmB,KADjC,cAAc,OAAO,SACyB,CAAC;IAChE,MAAM,WAAW,sBAAsB,KAAK;IAC5C,MAAM,WAAW;KACf,IAAI;KACJ;KACA;KACA,MAAM,OAAO;KACb;KACD;AAED,QAAI,aAAa,SAAS,CAExB,QAAO;KACL,SAAS,CAAC;MAAE,MAAM;MAAQ,MAFf,OAAO,SAAS,OAEG;MAAE,CAAC;KACjC,SAAS;MAAE,GAAG;MAAU,MAAM;MAAQ;KACvC;AAGH,WAAO;KACL,SAAS,CACP;MACE,MAAM;MACN,MAAM,KAAK,UAAU;OACnB,GAAG;OACH,MAAM;OACN,QAAQ,OAAO,SAAS,SAAS;OAClC,EAAE,MAAM,EAAE;MACZ,CACF;KACD,SAAS;MAAE,GAAG;MAAU,MAAM;MAAU;KACzC;YACM,KAAK;IACZ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;AAChE,WAAO;KACL,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,qBAAqB;MAAW,CAAC;KACjE,SAAS;MAAE,IAAI;MAAO,OAAO;MAAS;KACvC;;;EAGN"}
@@ -0,0 +1,8 @@
1
+ export declare function isImageInboundAttachment(att: {
2
+ type?: string;
3
+ mimeType?: string;
4
+ }): boolean;
5
+ export declare function isVoiceInboundAttachment(att: {
6
+ type?: string;
7
+ mimeType?: string;
8
+ }): boolean;
@@ -0,0 +1,11 @@
1
+ //#region src/channels/attachments/attachment-classifiers.ts
2
+ function isImageInboundAttachment(att) {
3
+ return att.type === "image" || att.type === "photo" || att.mimeType?.startsWith("image/") === true;
4
+ }
5
+ function isVoiceInboundAttachment(att) {
6
+ return att.type === "voice" || att.type === "audio" || att.mimeType?.startsWith("audio/") === true;
7
+ }
8
+ //#endregion
9
+ export { isImageInboundAttachment, isVoiceInboundAttachment };
10
+
11
+ //# sourceMappingURL=attachment-classifiers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachment-classifiers.js","names":[],"sources":["../../../../src/channels/attachments/attachment-classifiers.ts"],"sourcesContent":["export function isImageInboundAttachment(att: {\n type?: string;\n mimeType?: string;\n}): boolean {\n return (\n att.type === 'image' ||\n att.type === 'photo' ||\n att.mimeType?.startsWith('image/') === true\n );\n}\n\nexport function isVoiceInboundAttachment(att: { type?: string; mimeType?: string }): boolean {\n return (\n att.type === 'voice' ||\n att.type === 'audio' ||\n att.mimeType?.startsWith('audio/') === true\n );\n}\n"],"mappings":";AAAA,SAAgB,yBAAyB,KAG7B;AACV,QACE,IAAI,SAAS,WACb,IAAI,SAAS,WACb,IAAI,UAAU,WAAW,SAAS,KAAK;;AAI3C,SAAgB,yBAAyB,KAAoD;AAC3F,QACE,IAAI,SAAS,WACb,IAAI,SAAS,WACb,IAAI,UAAU,WAAW,SAAS,KAAK"}
@@ -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 { join, resolve } from "path";
4
- import { mkdir, writeFile } from "fs/promises";
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 { join, resolve } from "path";
4
- import { mkdir, writeFile } from "fs/promises";
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"}