@xopcai/xopc 0.0.109 → 0.0.111

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 (346) hide show
  1. package/README.md +46 -4
  2. package/README.zh-CN.md +46 -4
  3. package/dist/browser-ext/manifest.json +1 -1
  4. package/dist/extensions/feishu/src/pairing-config-resolver.d.ts +1 -0
  5. package/dist/extensions/feishu/src/pairing-config-resolver.js +30 -0
  6. package/dist/extensions/feishu/src/pairing-config-resolver.js.map +1 -0
  7. package/dist/extensions/feishu/src/plugin.d.ts +1 -0
  8. package/dist/extensions/feishu/src/plugin.js +1 -0
  9. package/dist/extensions/feishu/src/plugin.js.map +1 -1
  10. package/dist/extensions/telegram/src/access-control.d.ts +2 -1
  11. package/dist/extensions/telegram/src/access-control.js.map +1 -1
  12. package/dist/extensions/telegram/src/account-manager.d.ts +2 -1
  13. package/dist/extensions/telegram/src/account-manager.js.map +1 -1
  14. package/dist/extensions/telegram/src/adapters/gateway-setup.js +26 -26
  15. package/dist/extensions/telegram/src/adapters/gateway-setup.js.map +1 -1
  16. package/dist/extensions/telegram/src/adapters/onboard-cli.js +8 -4
  17. package/dist/extensions/telegram/src/adapters/onboard-cli.js.map +1 -1
  18. package/dist/extensions/telegram/src/config-schema.d.ts +88 -50
  19. package/dist/extensions/telegram/src/config-schema.js +40 -32
  20. package/dist/extensions/telegram/src/config-schema.js.map +1 -1
  21. package/dist/extensions/telegram/src/config-types.d.ts +54 -0
  22. package/dist/extensions/telegram/src/config-types.js +1 -0
  23. package/dist/extensions/telegram/src/doctor.js +17 -16
  24. package/dist/extensions/telegram/src/doctor.js.map +1 -1
  25. package/dist/extensions/telegram/src/exec-approvals.js +2 -3
  26. package/dist/extensions/telegram/src/exec-approvals.js.map +1 -1
  27. package/dist/extensions/telegram/src/group-config-resolver.d.ts +1 -1
  28. package/dist/extensions/telegram/src/group-config-resolver.js.map +1 -1
  29. package/dist/extensions/telegram/src/pairing-config-resolver.d.ts +1 -0
  30. package/dist/extensions/telegram/src/pairing-config-resolver.js +56 -0
  31. package/dist/extensions/telegram/src/pairing-config-resolver.js.map +1 -0
  32. package/dist/extensions/telegram/src/plugin.d.ts +1 -0
  33. package/dist/extensions/telegram/src/plugin.js +8 -9
  34. package/dist/extensions/telegram/src/plugin.js.map +1 -1
  35. package/dist/extensions/telegram/xopc.extension.json +156 -122
  36. package/dist/extensions/weixin/src/pairing-config-resolver.d.ts +1 -0
  37. package/dist/extensions/weixin/src/pairing-config-resolver.js +54 -0
  38. package/dist/extensions/weixin/src/pairing-config-resolver.js.map +1 -0
  39. package/dist/extensions/weixin/src/plugin.d.ts +1 -0
  40. package/dist/extensions/weixin/src/plugin.js +1 -0
  41. package/dist/extensions/weixin/src/plugin.js.map +1 -1
  42. package/dist/gateway/static/root/assets/Combination-BzaXOLd9.js +41 -0
  43. package/dist/gateway/static/root/assets/agent-avatar-dicebear-zlx9l5Yp.js +1 -0
  44. package/dist/gateway/static/root/assets/agent-browser-settings-page-B-0zGjt8.js +6 -0
  45. package/dist/gateway/static/root/assets/agent-defaults-tabbed-page-Bz7JWjRM.js +1 -0
  46. package/dist/gateway/static/root/assets/agents-DwDnJlXT.js +71 -0
  47. package/dist/gateway/static/root/assets/app-management-settings-panel-DaQwdeCq.js +1 -0
  48. package/dist/gateway/static/root/assets/appearance-settings-D2zv6r3P.js +1 -0
  49. package/dist/gateway/static/root/assets/apps-page-BIppe_jj.js +1 -0
  50. package/dist/gateway/static/root/assets/archive-plugin-DEmu8eH7.js +1 -0
  51. package/dist/gateway/static/root/assets/attachment-load-B98lJoN5.js +2 -0
  52. package/dist/gateway/static/root/assets/{attachment-preview-renderer-CybMSIoS.js → attachment-preview-renderer-DonnL2dL.js} +1 -1
  53. package/dist/gateway/static/root/assets/binary-plugins-CL9QKmyo.js +2 -0
  54. package/dist/gateway/static/root/assets/block-editor-BOAhMAhT.js +180 -0
  55. package/dist/gateway/static/root/assets/channels-settings-Br8iRnXp.js +1 -0
  56. package/dist/gateway/static/root/assets/cn-JdwAhgNB.js +1 -0
  57. package/dist/gateway/static/root/assets/code-editor-dZTY2dRW.js +1 -0
  58. package/dist/gateway/static/root/assets/config-api-0o1WYdz2.js +3 -0
  59. package/dist/gateway/static/root/assets/connectors-settings-DDvHo9in.js +2 -0
  60. package/dist/gateway/static/root/assets/cron-api-Dj_UZKST.js +1 -0
  61. package/dist/gateway/static/root/assets/cron-dreaming-jobs-nCMWCf0p.js +1 -0
  62. package/dist/gateway/static/root/assets/cron-page-Dv7GII9G.js +1 -0
  63. package/dist/gateway/static/root/assets/cron-schedule-picker-DH10KgD6.js +1 -0
  64. package/dist/gateway/static/root/assets/dist-By6Rh2ee.js +1 -0
  65. package/dist/gateway/static/root/assets/dist-CkobR7mk.js +1 -0
  66. package/dist/gateway/static/root/assets/dist-DokQNVMQ.js +1 -0
  67. package/dist/gateway/static/root/assets/dreaming-scene-BHMRVEUS.js +4585 -0
  68. package/dist/gateway/static/root/assets/dreaming-settings-L2rgZDQl.js +1 -0
  69. package/dist/gateway/static/root/assets/extension-debug-page-Cjx6pZVz.js +1 -0
  70. package/dist/gateway/static/root/assets/{extension-page-DNcTendk.js → extension-page-BSPNL90V.js} +1 -1
  71. package/dist/gateway/static/root/assets/extension-settings-page-m_UQYkVc.js +1 -0
  72. package/dist/gateway/static/root/assets/fetch-Bs7in_4F.js +1 -0
  73. package/dist/gateway/static/root/assets/format-interval-label-CftJ5uXu.js +1 -0
  74. package/dist/gateway/static/root/assets/gateway-config-api-Clfck-hW.js +1 -0
  75. package/dist/gateway/static/root/assets/gateway-settings-CS177xni.js +1 -0
  76. package/dist/gateway/static/root/assets/gateway-startup-retry-BkjVJc38.js +1 -0
  77. package/dist/gateway/static/root/assets/goal-detail-page-kqJue0sE.js +1 -0
  78. package/dist/gateway/static/root/assets/goals-page-C0FujUB5.js +2 -0
  79. package/dist/gateway/static/root/assets/goals-settings-CJnzNSDo.js +1 -0
  80. package/dist/gateway/static/root/assets/heartbeat-config-api-Cp7CPuIn.js +1 -0
  81. package/dist/gateway/static/root/assets/heartbeat-settings-ifSI1Wbh.js +1 -0
  82. package/dist/gateway/static/root/assets/html-workspace-editor-CQLWDiwE.js +1 -0
  83. package/dist/gateway/static/root/assets/index-COkF54xv.js +121 -0
  84. package/dist/gateway/static/root/assets/index-DIaSNGEO.css +1 -0
  85. package/dist/gateway/static/root/assets/interval-presets-B29Ss_2y.js +2 -0
  86. package/dist/gateway/static/root/assets/keyboard-shortcuts-settings--MI1rYl7.js +1 -0
  87. package/dist/gateway/static/root/assets/locale-store-DjbfSsfA.js +1 -0
  88. package/dist/gateway/static/root/assets/logs-page-BwrVNlyW.js +2 -0
  89. package/dist/gateway/static/root/assets/markdown-editor-AssmZFx4.js +2 -0
  90. package/dist/gateway/static/root/assets/markdown-split-B_oc9lJe.js +1 -0
  91. package/dist/gateway/static/root/assets/media-plugins-DLPvB5H3.js +1 -0
  92. package/dist/gateway/static/root/assets/mermaid-render-BbqDc_LQ.js +134 -0
  93. package/dist/gateway/static/root/assets/messages-Csef1aW7.js +3 -0
  94. package/dist/gateway/static/root/assets/middleware-Pd0e8Igk.js +1 -0
  95. package/dist/gateway/static/root/assets/models-hub-panel-DHGiTIvf.js +2 -0
  96. package/dist/gateway/static/root/assets/note-detail-page-BM7zlDiT.js +2 -0
  97. package/dist/gateway/static/root/assets/note-image-lightbox-rSqAgOi9.js +1 -0
  98. package/dist/gateway/static/root/assets/note-time-hm6JO_Wi.js +1 -0
  99. package/dist/gateway/static/root/assets/notes-page-C9ACGEty.js +1 -0
  100. package/dist/gateway/static/root/assets/preview-open-alternatives-Bw_N2oZi.js +1 -0
  101. package/dist/gateway/static/root/assets/remote-access-hub-BhcWIKuM.js +1 -0
  102. package/dist/gateway/static/root/assets/save-bar-controls-CNP2jmw0.js +1 -0
  103. package/dist/gateway/static/root/assets/schedule-field-BPAWui3P.js +1 -0
  104. package/dist/gateway/static/root/assets/schedule-summary-B-hdSRdR.js +1 -0
  105. package/dist/gateway/static/root/assets/schema-form-CDPzua9q.js +1 -0
  106. package/dist/gateway/static/root/assets/sessions-page-DzkpSDZr.js +1 -0
  107. package/dist/gateway/static/root/assets/settings-advanced-gate-BYS-rpzn.js +1 -0
  108. package/dist/gateway/static/root/assets/settings-collapsible-section-C9bff85B.js +1 -0
  109. package/dist/gateway/static/root/assets/settings-form-section-BsrK7TBF.js +1 -0
  110. package/dist/gateway/static/root/assets/settings-page-DWK4Zo7s.js +1 -0
  111. package/dist/gateway/static/root/assets/setup-status-panel-DJiksNu4.js +1 -0
  112. package/dist/gateway/static/root/assets/share-preview-page-DnutoY_S.js +2 -0
  113. package/dist/gateway/static/root/assets/shares-settings-6oRgJMpn.js +1 -0
  114. package/dist/gateway/static/root/assets/skill-reload-api-CU9i0nJV.js +1 -0
  115. package/dist/gateway/static/root/assets/skills-page-ERbdWENm.js +2 -0
  116. package/dist/gateway/static/root/assets/system-settings-panel-qP1JDtDd.js +1 -0
  117. package/dist/gateway/static/root/assets/theme-store-CD2wtgZV.js +1 -0
  118. package/dist/gateway/static/root/assets/use-agent-defaults-form-D3FGVLZb.js +1 -0
  119. package/dist/gateway/static/root/assets/use-blob-object-url-D4BFuzZB.js +1 -0
  120. package/dist/gateway/static/root/assets/use-channel-catalog-C4mg1hjD.js +1 -0
  121. package/dist/gateway/static/root/assets/use-settings-tab-guard-F6C0mpJt.js +1 -0
  122. package/dist/gateway/static/root/assets/vendor-codemirror-CNP1wPe7.js +45 -0
  123. package/dist/gateway/static/root/assets/voice-api-key-field-BnDDb6qO.js +1 -0
  124. package/dist/gateway/static/root/assets/voice-player-store-B2EwIhtY.js +1 -0
  125. package/dist/gateway/static/root/assets/{workflow-page.utils-BcZQmKBX.js → workflow-page.utils-CRB7WX9l.js} +1 -1
  126. package/dist/gateway/static/root/assets/workflows-page-2Fptr0je.js +18 -0
  127. package/dist/gateway/static/root/index.html +12 -10
  128. package/dist/package.js +1 -1
  129. package/dist/src/agent/goals/checklist-judge.d.ts +5 -0
  130. package/dist/src/agent/goals/checklist-judge.js +26 -2
  131. package/dist/src/agent/goals/checklist-judge.js.map +1 -1
  132. package/dist/src/agent/goals/evaluate-turn.d.ts +5 -1
  133. package/dist/src/agent/goals/evaluate-turn.js +29 -3
  134. package/dist/src/agent/goals/evaluate-turn.js.map +1 -1
  135. package/dist/src/agent/goals/goal-locale.d.ts +0 -2
  136. package/dist/src/agent/goals/goal-locale.js +2 -15
  137. package/dist/src/agent/goals/goal-locale.js.map +1 -1
  138. package/dist/src/agent/goals/persistent-goal-service.d.ts +9 -0
  139. package/dist/src/agent/goals/persistent-goal-service.js +2 -1
  140. package/dist/src/agent/goals/persistent-goal-service.js.map +1 -1
  141. package/dist/src/agent/goals/post-turn.d.ts +7 -0
  142. package/dist/src/agent/goals/post-turn.js +68 -23
  143. package/dist/src/agent/goals/post-turn.js.map +1 -1
  144. package/dist/src/agent/goals/state.d.ts +0 -5
  145. package/dist/src/agent/goals/state.js +1 -96
  146. package/dist/src/agent/goals/state.js.map +1 -1
  147. package/dist/src/agent/messaging/command-handler.d.ts +4 -0
  148. package/dist/src/agent/messaging/command-handler.js +6 -2
  149. package/dist/src/agent/messaging/command-handler.js.map +1 -1
  150. package/dist/src/agent/service/direct-turn-helpers.d.ts +2 -0
  151. package/dist/src/agent/service/direct-turn-helpers.js +3 -2
  152. package/dist/src/agent/service/direct-turn-helpers.js.map +1 -1
  153. package/dist/src/agent/service/process-direct-streaming.js +10 -1
  154. package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
  155. package/dist/src/agent/service.js +20 -1
  156. package/dist/src/agent/service.js.map +1 -1
  157. package/dist/src/agent/service.types.d.ts +8 -0
  158. package/dist/src/agent/tools/cronjob-tool.d.ts +2 -0
  159. package/dist/src/agent/tools/cronjob-tool.js +27 -4
  160. package/dist/src/agent/tools/cronjob-tool.js.map +1 -1
  161. package/dist/src/agent/tools/edit.d.ts +2 -0
  162. package/dist/src/agent/tools/edit.js +10 -0
  163. package/dist/src/agent/tools/edit.js.map +1 -1
  164. package/dist/src/agent/tools/factory.js +16 -3
  165. package/dist/src/agent/tools/factory.js.map +1 -1
  166. package/dist/src/agent/tools/goal-evidence-recorder.d.ts +6 -0
  167. package/dist/src/agent/tools/goal-evidence-recorder.js +34 -0
  168. package/dist/src/agent/tools/goal-evidence-recorder.js.map +1 -0
  169. package/dist/src/agent/tools/goal-tool.d.ts +19 -0
  170. package/dist/src/agent/tools/goal-tool.js +101 -0
  171. package/dist/src/agent/tools/goal-tool.js.map +1 -0
  172. package/dist/src/agent/tools/index.d.ts +1 -0
  173. package/dist/src/agent/tools/index.js +2 -1
  174. package/dist/src/agent/tools/shell.d.ts +2 -0
  175. package/dist/src/agent/tools/shell.js +17 -2
  176. package/dist/src/agent/tools/shell.js.map +1 -1
  177. package/dist/src/agent/tools/workflow-tool.d.ts +1 -0
  178. package/dist/src/agent/tools/workflow-tool.js +10 -2
  179. package/dist/src/agent/tools/workflow-tool.js.map +1 -1
  180. package/dist/src/agent/tools/write.d.ts +2 -0
  181. package/dist/src/agent/tools/write.js +11 -0
  182. package/dist/src/agent/tools/write.js.map +1 -1
  183. package/dist/src/channels/attachments/inbound-persist.d.ts +7 -0
  184. package/dist/src/channels/attachments/inbound-persist.js +20 -2
  185. package/dist/src/channels/attachments/inbound-persist.js.map +1 -1
  186. package/dist/src/channels/channel-domain.d.ts +1 -81
  187. package/dist/src/channels/pairing/index.d.ts +2 -2
  188. package/dist/src/channels/pairing/index.js +2 -2
  189. package/dist/src/channels/pairing/pairing-service.d.ts +13 -0
  190. package/dist/src/channels/pairing/pairing-service.js +78 -63
  191. package/dist/src/channels/pairing/pairing-service.js.map +1 -1
  192. package/dist/src/chat-commands/builtins/config.js +4 -11
  193. package/dist/src/chat-commands/builtins/config.js.map +1 -1
  194. package/dist/src/chat-commands/builtins/goal.d.ts +1 -1
  195. package/dist/src/chat-commands/builtins/goal.js +55 -97
  196. package/dist/src/chat-commands/builtins/goal.js.map +1 -1
  197. package/dist/src/chat-commands/builtins/subgoal.d.ts +1 -1
  198. package/dist/src/chat-commands/builtins/subgoal.js +49 -108
  199. package/dist/src/chat-commands/builtins/subgoal.js.map +1 -1
  200. package/dist/src/chat-commands/builtins/workflow.js +132 -8
  201. package/dist/src/chat-commands/builtins/workflow.js.map +1 -1
  202. package/dist/src/chat-commands/context.d.ts +3 -0
  203. package/dist/src/chat-commands/context.js +5 -0
  204. package/dist/src/chat-commands/context.js.map +1 -1
  205. package/dist/src/chat-commands/types.d.ts +5 -0
  206. package/dist/src/cli/command-catalog.js +43 -0
  207. package/dist/src/cli/command-catalog.js.map +1 -1
  208. package/dist/src/cli/command-loaders.js +1 -0
  209. package/dist/src/cli/command-loaders.js.map +1 -1
  210. package/dist/src/cli/command-manifest.js +4 -0
  211. package/dist/src/cli/command-manifest.js.map +1 -1
  212. package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
  213. package/dist/src/cli/commands/goal.d.ts +4 -0
  214. package/dist/src/cli/commands/goal.js +213 -0
  215. package/dist/src/cli/commands/goal.js.map +1 -0
  216. package/dist/src/cli/commands/onboard.js +11 -5
  217. package/dist/src/cli/commands/onboard.js.map +1 -1
  218. package/dist/src/config/index.js +2 -2
  219. package/dist/src/config/schema.d.ts +115 -0
  220. package/dist/src/config/schema.js +72 -18
  221. package/dist/src/config/schema.js.map +1 -1
  222. package/dist/src/config/web-patch.d.ts +14 -0
  223. package/dist/src/config/web-patch.js +45 -6
  224. package/dist/src/config/web-patch.js.map +1 -1
  225. package/dist/src/cron/executor.d.ts +2 -0
  226. package/dist/src/cron/executor.js +25 -0
  227. package/dist/src/cron/executor.js.map +1 -1
  228. package/dist/src/cron/job-content.js +1 -0
  229. package/dist/src/cron/job-content.js.map +1 -1
  230. package/dist/src/cron/types.d.ts +19 -1
  231. package/dist/src/cron/validation.d.ts +20 -0
  232. package/dist/src/cron/validation.js +7 -0
  233. package/dist/src/cron/validation.js.map +1 -1
  234. package/dist/src/gateway/hono/lib/config-payload.d.ts +14 -0
  235. package/dist/src/gateway/hono/routes/goals.d.ts +1 -1
  236. package/dist/src/gateway/hono/routes/goals.js +670 -173
  237. package/dist/src/gateway/hono/routes/goals.js.map +1 -1
  238. package/dist/src/gateway/hono/routes/home.d.ts +5 -0
  239. package/dist/src/gateway/hono/routes/home.js +88 -3
  240. package/dist/src/gateway/hono/routes/home.js.map +1 -1
  241. package/dist/src/gateway/hono/routes/notes.js +20 -14
  242. package/dist/src/gateway/hono/routes/notes.js.map +1 -1
  243. package/dist/src/gateway/hono/routes/workflows.js +6 -2
  244. package/dist/src/gateway/hono/routes/workflows.js.map +1 -1
  245. package/dist/src/gateway/hono/sse.js.map +1 -1
  246. package/dist/src/gateway/service/agent-runner.d.ts +2 -0
  247. package/dist/src/gateway/service/agent-runner.js +6 -2
  248. package/dist/src/gateway/service/agent-runner.js.map +1 -1
  249. package/dist/src/gateway/service/run-gateway-agent.d.ts +3 -0
  250. package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
  251. package/dist/src/gateway/service.d.ts +8 -0
  252. package/dist/src/gateway/service.js +70 -3
  253. package/dist/src/gateway/service.js.map +1 -1
  254. package/dist/src/goals/goal-notifications.d.ts +29 -0
  255. package/dist/src/goals/goal-notifications.js +154 -0
  256. package/dist/src/goals/goal-notifications.js.map +1 -0
  257. package/dist/src/goals/goal-queue-store.d.ts +16 -0
  258. package/dist/src/goals/goal-queue-store.js +131 -0
  259. package/dist/src/goals/goal-queue-store.js.map +1 -0
  260. package/dist/src/goals/goal-queue-types.d.ts +31 -0
  261. package/dist/src/goals/goal-queue-types.js +1 -0
  262. package/dist/src/goals/goal-runner.d.ts +17 -0
  263. package/dist/src/goals/goal-runner.js +127 -0
  264. package/dist/src/goals/goal-runner.js.map +1 -0
  265. package/dist/src/goals/goal-service.d.ts +77 -0
  266. package/dist/src/goals/goal-service.js +261 -0
  267. package/dist/src/goals/goal-service.js.map +1 -0
  268. package/dist/src/goals/goal-store.d.ts +38 -0
  269. package/dist/src/goals/goal-store.js +404 -0
  270. package/dist/src/goals/goal-store.js.map +1 -0
  271. package/dist/src/goals/goal-workflow-judge.d.ts +13 -0
  272. package/dist/src/goals/goal-workflow-judge.js +147 -0
  273. package/dist/src/goals/goal-workflow-judge.js.map +1 -0
  274. package/dist/src/goals/index.d.ts +10 -0
  275. package/dist/src/goals/index.js +7 -0
  276. package/dist/src/goals/types.d.ts +119 -0
  277. package/dist/src/goals/types.js +1 -0
  278. package/dist/src/storage/sqlite/migrations/006_add_goals.sql +108 -0
  279. package/dist/src/storage/sqlite/migrations/007_goal_run_judge_schema.sql +4 -0
  280. package/dist/src/storage/sqlite/migrations/008_goal_queue_workflow_runs.sql +53 -0
  281. package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
  282. package/dist/src/storage/sqlite/migrations/runner.js +3 -3
  283. package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
  284. package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
  285. package/dist/src/storage/sqlite/schema.js +1 -1
  286. package/dist/src/storage/sqlite/schema.sql +163 -0
  287. package/dist/src/utils/logger/stats.d.ts +1 -1
  288. package/dist/src/workflows/domain/run.d.ts +1 -0
  289. package/dist/src/workflows/domain/run.js.map +1 -1
  290. package/dist/src/workflows/service/workflow-run-service.d.ts +1 -0
  291. package/dist/src/workflows/service/workflow-run-service.js +5 -1
  292. package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
  293. package/dist/src/workflows/service/workflow-run-service.types.d.ts +1 -0
  294. package/dist/src/workflows/service/workflow-session-bridge.js +7 -0
  295. package/dist/src/workflows/service/workflow-session-bridge.js.map +1 -1
  296. package/dist/src/workflows/store/run-index-store.d.ts +8 -0
  297. package/dist/src/workflows/store/run-index-store.js +58 -0
  298. package/dist/src/workflows/store/run-index-store.js.map +1 -0
  299. package/dist/src/workflows/store/run-store.d.ts +2 -0
  300. package/dist/src/workflows/store/run-store.js +14 -21
  301. package/dist/src/workflows/store/run-store.js.map +1 -1
  302. package/package.json +9 -3
  303. package/dist/gateway/static/root/assets/Combination-HAlzriaz.js +0 -41
  304. package/dist/gateway/static/root/assets/agents-D2VtykeA.js +0 -78
  305. package/dist/gateway/static/root/assets/apps-page-BPQ6ueG-.js +0 -1
  306. package/dist/gateway/static/root/assets/channels-settings-DgZc25cS.js +0 -1
  307. package/dist/gateway/static/root/assets/circle-check-C23XjkUj.js +0 -1
  308. package/dist/gateway/static/root/assets/copy-Dv6d4Dvw.js +0 -1
  309. package/dist/gateway/static/root/assets/cron-page-BQFw5O-8.js +0 -1
  310. package/dist/gateway/static/root/assets/dist-BLMShn4_.js +0 -1
  311. package/dist/gateway/static/root/assets/extension-debug-page-BbQ8uZY_.js +0 -1
  312. package/dist/gateway/static/root/assets/extension-settings-page-ClWJvwHK.js +0 -1
  313. package/dist/gateway/static/root/assets/fetch-CFdKBnE6.js +0 -1
  314. package/dist/gateway/static/root/assets/gateway-startup-retry-DdngdObI.js +0 -1
  315. package/dist/gateway/static/root/assets/heartbeat-config-api-4O1U_Oki.js +0 -1
  316. package/dist/gateway/static/root/assets/index-DI5_GsZt.css +0 -1
  317. package/dist/gateway/static/root/assets/index-S23p3aa0.js +0 -4845
  318. package/dist/gateway/static/root/assets/logs-page-BAIdbvSC.js +0 -2
  319. package/dist/gateway/static/root/assets/note-detail-page-BVDoLwec.js +0 -180
  320. package/dist/gateway/static/root/assets/note-time-COWUfBZO.js +0 -1
  321. package/dist/gateway/static/root/assets/notes-page-CI3aD7Tm.js +0 -1
  322. package/dist/gateway/static/root/assets/schema-form-CDpBshDP.js +0 -1
  323. package/dist/gateway/static/root/assets/sessions-page-DM1S4eso.js +0 -1
  324. package/dist/gateway/static/root/assets/settings-advanced-gate-Bz_nYzvB.js +0 -2
  325. package/dist/gateway/static/root/assets/settings-form-section-BxhPoWWH.js +0 -1
  326. package/dist/gateway/static/root/assets/settings-page-Ch_Sd28M.js +0 -3
  327. package/dist/gateway/static/root/assets/share-preview-page-D6u4llqD.js +0 -2
  328. package/dist/gateway/static/root/assets/skills-page-XMi76Nps.js +0 -2
  329. package/dist/gateway/static/root/assets/theme-store-BZozwAeK.js +0 -1
  330. package/dist/gateway/static/root/assets/url-DQRqHuL-.js +0 -3
  331. package/dist/gateway/static/root/assets/vendor-codemirror-DYoKfS8f.js +0 -45
  332. package/dist/gateway/static/root/assets/voice-api-key-field-CMt5Xc7_.js +0 -1
  333. package/dist/gateway/static/root/assets/workflows-page-CxFbPBed.js +0 -18
  334. package/dist/src/agent/goals/checklist-user.d.ts +0 -24
  335. package/dist/src/agent/goals/checklist-user.js +0 -92
  336. package/dist/src/agent/goals/checklist-user.js.map +0 -1
  337. package/dist/src/agent/goals/goal-run-store.d.ts +0 -19
  338. package/dist/src/agent/goals/goal-run-store.js +0 -138
  339. package/dist/src/agent/goals/goal-run-store.js.map +0 -1
  340. package/dist/src/agent/goals/goal-run-types.d.ts +0 -28
  341. package/dist/src/agent/goals/goal-run-types.js +0 -6
  342. package/dist/src/agent/goals/goal-run-types.js.map +0 -1
  343. package/dist/src/agent/goals/patch-from-user-action.d.ts +0 -20
  344. package/dist/src/agent/goals/patch-from-user-action.js +0 -103
  345. package/dist/src/agent/goals/patch-from-user-action.js.map +0 -1
  346. /package/dist/gateway/static/root/assets/{note-detail-page-D4ZIVQbk.css → block-editor-D4ZIVQbk.css} +0 -0
@@ -1,7 +1,9 @@
1
1
  import type { AgentTool } from '@earendil-works/pi-agent-core';
2
+ import type { GoalEvidenceRecordInput } from './goal-evidence-recorder.js';
2
3
  export interface CreateWriteFileToolOptions {
3
4
  /** When set and the path is a bare profile filename (e.g. SOUL.md), write to this root. */
4
5
  profileMarkdownRoot?: string;
6
+ recordGoalEvidence?: (input: GoalEvidenceRecordInput) => Promise<void> | void;
5
7
  }
6
8
  export declare function createWriteFileTool(workspace: string, options?: CreateWriteFileToolOptions): AgentTool;
7
9
  export declare const writeFileTool: AgentTool;
@@ -52,6 +52,17 @@ function createWriteFileTool(workspace, options) {
52
52
  const target = writesProfileFile ? resolveProfileMarkdownPathIfBareName(p.path, options.profileMarkdownRoot) : resolvePathUnderWorkspace(p.path, workspace);
53
53
  await mkdir(dirname(target), { recursive: true });
54
54
  await writeFile(target, p.content, "utf-8");
55
+ await options?.recordGoalEvidence?.({
56
+ kind: "file",
57
+ title: `File written: ${target}`,
58
+ summary: `${contentBytes} bytes written`,
59
+ uri: target,
60
+ data: {
61
+ path: target,
62
+ size: contentBytes,
63
+ operation: "write"
64
+ }
65
+ });
55
66
  return {
56
67
  content: [{
57
68
  type: "text",
@@ -1 +1 @@
1
- {"version":3,"file":"write.js","names":[],"sources":["../../../../src/agent/tools/write.ts"],"sourcesContent":["// Write file tool\nimport { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\nimport { writeFile, mkdir } from 'fs/promises';\nimport { dirname } from 'path';\nimport { checkFileSafety } from '../prompt/safety.js';\nimport {\n isBareProfileMarkdownFileName,\n resolveProfileMarkdownPathIfBareName,\n resolvePathUnderWorkspace,\n} from './tool-paths.js';\nimport { evaluateFilePolicy } from '../sandbox/exec-policy.js';\n\nconst MAX_FILE_SIZE = 10 * 1024 * 1024;\n\nconst WriteFileSchema = Type.Object({\n path: Type.String({ description: 'File path to write' }),\n content: Type.String({ description: 'Content to write' }),\n});\n\ntype WriteFileParams = { path: string; content: string };\n\nexport interface CreateWriteFileToolOptions {\n /** When set and the path is a bare profile filename (e.g. SOUL.md), write to this root. */\n profileMarkdownRoot?: string;\n}\n\nexport function createWriteFileTool(\n workspace: string,\n options?: CreateWriteFileToolOptions,\n): AgentTool {\n return {\n name: 'write_file',\n description:\n 'Create or overwrite a file. Relative paths are under the current agent workspace; profile Markdown (SOUL.md, etc.) is written automatically when given by filename.',\n parameters: WriteFileSchema,\n label: '📝 Write',\n\n async execute(\n _toolCallId: string,\n params: any,\n _signal?: AbortSignal,\n ): Promise<AgentToolResult<{}>> {\n try {\n const p = params as WriteFileParams;\n const safety = checkFileSafety('write', p.path);\n if (!safety.allowed) {\n return { content: [{ type: 'text', text: `🚫 ${safety.message}` }], details: {} };\n }\n\n const writesProfileFile = Boolean(\n options?.profileMarkdownRoot && isBareProfileMarkdownFileName(p.path),\n );\n const workspaceRoot = writesProfileFile ? options!.profileMarkdownRoot! : workspace;\n\n // Sandbox path-policy check (blocked dirs, symlink escape, config protection)\n const pathPolicy = evaluateFilePolicy({\n operation: 'write',\n path: p.path,\n workspaceRoot,\n });\n if (!pathPolicy.allowed) {\n return { content: [{ type: 'text', text: `🚫 Sandbox: ${pathPolicy.reason}` }], details: {} };\n }\n\n const contentBytes = Buffer.byteLength(p.content, 'utf-8');\n if (contentBytes > MAX_FILE_SIZE) {\n return { content: [{ type: 'text', text: `🚫 File too large: ${contentBytes} bytes` }], details: {} };\n }\n\n const target = writesProfileFile\n ? resolveProfileMarkdownPathIfBareName(p.path, options!.profileMarkdownRoot!)\n : resolvePathUnderWorkspace(p.path, workspace);\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, p.content, 'utf-8');\n return { content: [{ type: 'text', text: `File written: ${target}` }], details: { size: contentBytes } };\n } catch (error) {\n return { content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }], details: {} };\n }\n },\n } as any;\n}\n\nexport const writeFileTool: AgentTool = createWriteFileTool(process.cwd());\n"],"mappings":";;;;;;;AAaA,MAAM,gBAAgB,KAAK,OAAO;AAElC,MAAM,kBAAkB,KAAK,OAAO;CAClC,MAAM,KAAK,OAAO,EAAE,aAAa,sBAAsB,CAAC;CACxD,SAAS,KAAK,OAAO,EAAE,aAAa,oBAAoB,CAAC;CAC1D,CAAC;AASF,SAAgB,oBACd,WACA,SACW;AACX,QAAO;EACL,MAAM;EACN,aACE;EACF,YAAY;EACZ,OAAO;EAEP,MAAM,QACJ,aACA,QACA,SAC8B;AAC9B,OAAI;IACF,MAAM,IAAI;IACV,MAAM,SAAS,gBAAgB,SAAS,EAAE,KAAK;AAC/C,QAAI,CAAC,OAAO,QACV,QAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,MAAM,OAAO;MAAW,CAAC;KAAE,SAAS,EAAE;KAAE;IAGnF,MAAM,oBAAoB,QACxB,SAAS,uBAAuB,8BAA8B,EAAE,KAAK,CACtE;IACD,MAAM,gBAAgB,oBAAoB,QAAS,sBAAuB;IAG1E,MAAM,aAAa,mBAAmB;KACpC,WAAW;KACX,MAAM,EAAE;KACR;KACD,CAAC;AACF,QAAI,CAAC,WAAW,QACd,QAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,eAAe,WAAW;MAAU,CAAC;KAAE,SAAS,EAAE;KAAE;IAG/F,MAAM,eAAe,OAAO,WAAW,EAAE,SAAS,QAAQ;AAC1D,QAAI,eAAe,cACjB,QAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,sBAAsB,aAAa;MAAS,CAAC;KAAE,SAAS,EAAE;KAAE;IAGvG,MAAM,SAAS,oBACX,qCAAqC,EAAE,MAAM,QAAS,oBAAqB,GAC3E,0BAA0B,EAAE,MAAM,UAAU;AAChD,UAAM,MAAM,QAAQ,OAAO,EAAE,EAAE,WAAW,MAAM,CAAC;AACjD,UAAM,UAAU,QAAQ,EAAE,SAAS,QAAQ;AAC3C,WAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,iBAAiB;MAAU,CAAC;KAAE,SAAS,EAAE,MAAM,cAAc;KAAE;YACjG,OAAO;AACd,WAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;MAAI,CAAC;KAAE,SAAS,EAAE;KAAE;;;EAGlI;;AAGH,MAAa,gBAA2B,oBAAoB,QAAQ,KAAK,CAAC"}
1
+ {"version":3,"file":"write.js","names":[],"sources":["../../../../src/agent/tools/write.ts"],"sourcesContent":["// Write file tool\nimport { Type } from '@sinclair/typebox';\nimport type { AgentTool, AgentToolResult } from '@earendil-works/pi-agent-core';\nimport { writeFile, mkdir } from 'fs/promises';\nimport { dirname } from 'path';\nimport { checkFileSafety } from '../prompt/safety.js';\nimport {\n isBareProfileMarkdownFileName,\n resolveProfileMarkdownPathIfBareName,\n resolvePathUnderWorkspace,\n} from './tool-paths.js';\nimport { evaluateFilePolicy } from '../sandbox/exec-policy.js';\nimport type { GoalEvidenceRecordInput } from './goal-evidence-recorder.js';\n\nconst MAX_FILE_SIZE = 10 * 1024 * 1024;\n\nconst WriteFileSchema = Type.Object({\n path: Type.String({ description: 'File path to write' }),\n content: Type.String({ description: 'Content to write' }),\n});\n\ntype WriteFileParams = { path: string; content: string };\n\nexport interface CreateWriteFileToolOptions {\n /** When set and the path is a bare profile filename (e.g. SOUL.md), write to this root. */\n profileMarkdownRoot?: string;\n recordGoalEvidence?: (input: GoalEvidenceRecordInput) => Promise<void> | void;\n}\n\nexport function createWriteFileTool(\n workspace: string,\n options?: CreateWriteFileToolOptions,\n): AgentTool {\n return {\n name: 'write_file',\n description:\n 'Create or overwrite a file. Relative paths are under the current agent workspace; profile Markdown (SOUL.md, etc.) is written automatically when given by filename.',\n parameters: WriteFileSchema,\n label: '📝 Write',\n\n async execute(\n _toolCallId: string,\n params: any,\n _signal?: AbortSignal,\n ): Promise<AgentToolResult<{}>> {\n try {\n const p = params as WriteFileParams;\n const safety = checkFileSafety('write', p.path);\n if (!safety.allowed) {\n return { content: [{ type: 'text', text: `🚫 ${safety.message}` }], details: {} };\n }\n\n const writesProfileFile = Boolean(\n options?.profileMarkdownRoot && isBareProfileMarkdownFileName(p.path),\n );\n const workspaceRoot = writesProfileFile ? options!.profileMarkdownRoot! : workspace;\n\n // Sandbox path-policy check (blocked dirs, symlink escape, config protection)\n const pathPolicy = evaluateFilePolicy({\n operation: 'write',\n path: p.path,\n workspaceRoot,\n });\n if (!pathPolicy.allowed) {\n return { content: [{ type: 'text', text: `🚫 Sandbox: ${pathPolicy.reason}` }], details: {} };\n }\n\n const contentBytes = Buffer.byteLength(p.content, 'utf-8');\n if (contentBytes > MAX_FILE_SIZE) {\n return { content: [{ type: 'text', text: `🚫 File too large: ${contentBytes} bytes` }], details: {} };\n }\n\n const target = writesProfileFile\n ? resolveProfileMarkdownPathIfBareName(p.path, options!.profileMarkdownRoot!)\n : resolvePathUnderWorkspace(p.path, workspace);\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, p.content, 'utf-8');\n await options?.recordGoalEvidence?.({\n kind: 'file',\n title: `File written: ${target}`,\n summary: `${contentBytes} bytes written`,\n uri: target,\n data: { path: target, size: contentBytes, operation: 'write' },\n });\n return { content: [{ type: 'text', text: `File written: ${target}` }], details: { size: contentBytes } };\n } catch (error) {\n return { content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }], details: {} };\n }\n },\n } as any;\n}\n\nexport const writeFileTool: AgentTool = createWriteFileTool(process.cwd());\n"],"mappings":";;;;;;;AAcA,MAAM,gBAAgB,KAAK,OAAO;AAElC,MAAM,kBAAkB,KAAK,OAAO;CAClC,MAAM,KAAK,OAAO,EAAE,aAAa,sBAAsB,CAAC;CACxD,SAAS,KAAK,OAAO,EAAE,aAAa,oBAAoB,CAAC;CAC1D,CAAC;AAUF,SAAgB,oBACd,WACA,SACW;AACX,QAAO;EACL,MAAM;EACN,aACE;EACF,YAAY;EACZ,OAAO;EAEP,MAAM,QACJ,aACA,QACA,SAC8B;AAC9B,OAAI;IACF,MAAM,IAAI;IACV,MAAM,SAAS,gBAAgB,SAAS,EAAE,KAAK;AAC/C,QAAI,CAAC,OAAO,QACV,QAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,MAAM,OAAO;MAAW,CAAC;KAAE,SAAS,EAAE;KAAE;IAGnF,MAAM,oBAAoB,QACxB,SAAS,uBAAuB,8BAA8B,EAAE,KAAK,CACtE;IACD,MAAM,gBAAgB,oBAAoB,QAAS,sBAAuB;IAG1E,MAAM,aAAa,mBAAmB;KACpC,WAAW;KACX,MAAM,EAAE;KACR;KACD,CAAC;AACF,QAAI,CAAC,WAAW,QACd,QAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,eAAe,WAAW;MAAU,CAAC;KAAE,SAAS,EAAE;KAAE;IAG/F,MAAM,eAAe,OAAO,WAAW,EAAE,SAAS,QAAQ;AAC1D,QAAI,eAAe,cACjB,QAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,sBAAsB,aAAa;MAAS,CAAC;KAAE,SAAS,EAAE;KAAE;IAGvG,MAAM,SAAS,oBACX,qCAAqC,EAAE,MAAM,QAAS,oBAAqB,GAC3E,0BAA0B,EAAE,MAAM,UAAU;AAChD,UAAM,MAAM,QAAQ,OAAO,EAAE,EAAE,WAAW,MAAM,CAAC;AACjD,UAAM,UAAU,QAAQ,EAAE,SAAS,QAAQ;AAC3C,UAAM,SAAS,qBAAqB;KAClC,MAAM;KACN,OAAO,iBAAiB;KACxB,SAAS,GAAG,aAAa;KACzB,KAAK;KACL,MAAM;MAAE,MAAM;MAAQ,MAAM;MAAc,WAAW;MAAS;KAC/D,CAAC;AACF,WAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,iBAAiB;MAAU,CAAC;KAAE,SAAS,EAAE,MAAM,cAAc;KAAE;YACjG,OAAO;AACd,WAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;MAAI,CAAC;KAAE,SAAS,EAAE;KAAE;;;EAGlI;;AAGH,MAAa,gBAA2B,oBAAoB,QAAQ,KAAK,CAAC"}
@@ -12,6 +12,13 @@ export declare function decodeInboundAttachmentBase64(data: string): Buffer;
12
12
  */
13
13
  export declare function persistInboundAttachments(attachments: InboundAttachmentInput[] | undefined, opts?: {
14
14
  maxBytes?: number;
15
+ resolveUri?: (uri: string, attachment: InboundAttachmentInput) => Promise<{
16
+ buffer: Buffer;
17
+ path: string;
18
+ mimeType: string;
19
+ name: string;
20
+ size: number;
21
+ } | null>;
15
22
  }): Promise<MediaRef[] | undefined>;
16
23
  /** Read attachment bytes by media URI (voice STT, tools). */
17
24
  export declare function readInboundAttachmentBuffer(uri: string): Promise<Buffer>;
@@ -23,8 +23,26 @@ async function persistInboundAttachments(attachments, opts) {
23
23
  const out = [];
24
24
  for (const att of attachments) {
25
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));
26
+ const uri = att.uri.trim();
27
+ if (uri.startsWith("media://")) {
28
+ const resolved = await resolveMediaReference(uri);
29
+ out.push(mediaRefFromUri(att, resolved.uri, resolved.path, resolved.bucket, resolved.id));
30
+ } else {
31
+ const resolved = await opts?.resolveUri?.(uri, att);
32
+ if (!resolved) throw new Error(`Inbound attachment "${att.name ?? "file"}" has unsupported uri`);
33
+ const saved = await saveMediaBuffer(resolved.buffer, {
34
+ contentType: resolved.mimeType,
35
+ bucket: "inbound",
36
+ maxBytes,
37
+ originalFilename: resolved.name
38
+ });
39
+ out.push(toMediaRef({
40
+ ...att,
41
+ mimeType: resolved.mimeType,
42
+ name: resolved.name,
43
+ size: resolved.size
44
+ }, saved));
45
+ }
28
46
  continue;
29
47
  }
30
48
  if (!att.data || att.data.length === 0) throw new Error(`Inbound attachment "${att.name ?? "file"}" is missing data and uri`);
@@ -1 +1 @@
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"}
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?: {\n maxBytes?: number;\n resolveUri?: (uri: string, attachment: InboundAttachmentInput) => Promise<{\n buffer: Buffer;\n path: string;\n mimeType: string;\n name: string;\n size: number;\n } | null>;\n },\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 uri = att.uri.trim();\n if (uri.startsWith('media://')) {\n const resolved = await resolveMediaReference(uri);\n out.push(\n mediaRefFromUri(att, resolved.uri, resolved.path, resolved.bucket, resolved.id),\n );\n } else {\n const resolved = await opts?.resolveUri?.(uri, att);\n if (!resolved) {\n throw new Error(`Inbound attachment \"${att.name ?? 'file'}\" has unsupported uri`);\n }\n const saved = await saveMediaBuffer(resolved.buffer, {\n contentType: resolved.mimeType,\n bucket: 'inbound',\n maxBytes,\n originalFilename: resolved.name,\n });\n out.push(toMediaRef({ ...att, mimeType: resolved.mimeType, name: resolved.name, size: resolved.size }, saved));\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,MAUiC;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,MAAM,IAAI,IAAI,MAAM;AAC1B,OAAI,IAAI,WAAW,WAAW,EAAE;IAC9B,MAAM,WAAW,MAAM,sBAAsB,IAAI;AACjD,QAAI,KACF,gBAAgB,KAAK,SAAS,KAAK,SAAS,MAAM,SAAS,QAAQ,SAAS,GAAG,CAChF;UACI;IACL,MAAM,WAAW,MAAM,MAAM,aAAa,KAAK,IAAI;AACnD,QAAI,CAAC,SACH,OAAM,IAAI,MAAM,uBAAuB,IAAI,QAAQ,OAAO,uBAAuB;IAEnF,MAAM,QAAQ,MAAM,gBAAgB,SAAS,QAAQ;KACnD,aAAa,SAAS;KACtB,QAAQ;KACR;KACA,kBAAkB,SAAS;KAC5B,CAAC;AACF,QAAI,KAAK,WAAW;KAAE,GAAG;KAAK,UAAU,SAAS;KAAU,MAAM,SAAS;KAAM,MAAM,SAAS;KAAM,EAAE,MAAM,CAAC;;AAEhH;;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"}
@@ -1,94 +1,14 @@
1
1
  /**
2
- * Channel domain types (Telegram config, message context, send options).
2
+ * Channel domain types shared by channel plugins.
3
3
  */
4
4
  import type { MessageBus } from '../infra/bus/index.js';
5
5
  import type { Config } from '../config/schema.js';
6
6
  import type { ProgressStage } from '../agent/lifecycle/progress.js';
7
7
  export type { ProgressStage } from '../agent/lifecycle/progress.js';
8
- export type ChannelType = 'telegram' | 'system';
9
8
  export type ChatType = 'direct' | 'group' | 'channel' | 'thread';
10
- export interface TelegramTopicConfig {
11
- topicId: string;
12
- requireMention?: boolean;
13
- enabled?: boolean;
14
- allowFrom?: Array<string | number>;
15
- systemPrompt?: string;
16
- agentId?: string;
17
- groupPolicy?: GroupPolicy;
18
- }
19
9
  export type DmPolicy = 'pairing' | 'allowlist' | 'open' | 'disabled';
20
10
  export type GroupPolicy = 'open' | 'disabled' | 'allowlist';
21
11
  export type ReplyToMode = 'off' | 'first' | 'all';
22
- export interface TelegramGroupConfig {
23
- groupId: string;
24
- requireMention?: boolean;
25
- groupPolicy?: GroupPolicy;
26
- enabled?: boolean;
27
- allowFrom?: Array<string | number>;
28
- systemPrompt?: string;
29
- agentId?: string;
30
- topics?: Record<string, TelegramTopicConfig>;
31
- }
32
- export interface TelegramAccountConfig {
33
- accountId: string;
34
- name?: string;
35
- enabled?: boolean;
36
- botToken?: string;
37
- tokenFile?: string;
38
- dmPolicy?: DmPolicy;
39
- groupPolicy?: GroupPolicy;
40
- replyToMode?: ReplyToMode;
41
- allowFrom?: Array<string | number>;
42
- groupAllowFrom?: Array<string | number>;
43
- groups?: Record<string, TelegramGroupConfig>;
44
- historyLimit?: number;
45
- textChunkLimit?: number;
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
- };
58
- proxy?: string;
59
- apiRoot?: string;
60
- pollingStallThresholdMs?: number;
61
- reactionLevel?: 'off' | 'ack' | 'minimal' | 'extensive';
62
- reactionNotifications?: 'off' | 'own' | 'all';
63
- ackReaction?: string;
64
- }
65
- export interface TelegramChannelConfig {
66
- enabled?: boolean;
67
- accounts?: Record<string, TelegramAccountConfig>;
68
- botToken?: string;
69
- allowFrom?: Array<string | number>;
70
- groupAllowFrom?: Array<string | number>;
71
- dmPolicy?: DmPolicy;
72
- groupPolicy?: GroupPolicy;
73
- replyToMode?: ReplyToMode;
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
- };
86
- historyLimit?: number;
87
- textChunkLimit?: number;
88
- proxy?: string;
89
- apiRoot?: string;
90
- debug?: boolean;
91
- }
92
12
  export interface NormalizedAllowFrom {
93
13
  entries: string[];
94
14
  hasWildcard: boolean;
@@ -9,8 +9,8 @@ export type { IssuePairingChallengeParams } from './pairing-challenge.js';
9
9
  export { approveChannelPairingFromCli } from './approve-cli.js';
10
10
  export { PAIRING_PENDING_MAX, PAIRING_STALE_PENDING_MS } from './pairing-constants.js';
11
11
  export { createStandardPairingAdapter } from './pairing-store-adapter.js';
12
- export { approveChannelPairing, approveChannelPairingBySender, listChannelPairingState, listChannelPairingSummary, collectPairingPendingIssues, revokeChannelPairingPaired, dismissChannelPairingPending, } from './pairing-service.js';
13
- export type { ChannelPairingState, ChannelPairingSummary, ChannelPairingSummaryEntry, PairingPendingIssue, PairingPendingView, } from './pairing-service.js';
12
+ export { approveChannelPairing, approveChannelPairingBySender, listChannelPairingState, listChannelPairingSummary, collectPairingPendingIssues, revokeChannelPairingPaired, dismissChannelPairingPending, registerPairingChannelResolver, } from './pairing-service.js';
13
+ export type { ChannelPairingState, ChannelPairingSummary, ChannelPairingSummaryEntry, PairingChannelConfigResolver, PairingPaths, PairingPendingIssue, PairingPendingView, } from './pairing-service.js';
14
14
  export { setPairingBroadcastSink, broadcastPairingEvent } from './pairing-events.js';
15
15
  export type { PairingBroadcastPayload } from './pairing-events.js';
16
16
  export { mergeDistinctSenderIds } from './preseed-allow-from.js';
@@ -5,8 +5,8 @@ import { PAIRING_PENDING_TTL_MS, approvePairingBySenderIdSync, approvePairingCod
5
5
  import { buildPairingInstructionText } from "./pairing-messages.js";
6
6
  import { broadcastPairingEvent, setPairingBroadcastSink } from "./pairing-events.js";
7
7
  import { issuePairingChallenge } from "./pairing-challenge.js";
8
- import { approveChannelPairing, approveChannelPairingBySender, collectPairingPendingIssues, dismissChannelPairingPending, listChannelPairingState, listChannelPairingSummary, revokeChannelPairingPaired } from "./pairing-service.js";
8
+ import { approveChannelPairing, approveChannelPairingBySender, collectPairingPendingIssues, dismissChannelPairingPending, listChannelPairingState, listChannelPairingSummary, registerPairingChannelResolver, revokeChannelPairingPaired } from "./pairing-service.js";
9
9
  import { approveChannelPairingFromCli } from "./approve-cli.js";
10
10
  import { createStandardPairingAdapter } from "./pairing-store-adapter.js";
11
11
  import { mergeDistinctSenderIds } from "./preseed-allow-from.js";
12
- export { PAIRING_PENDING_MAX, PAIRING_PENDING_TTL_MS, PAIRING_STALE_PENDING_MS, appendAllowFromIdSync, approveChannelPairing, approveChannelPairingBySender, approveChannelPairingFromCli, approvePairingBySenderIdSync, approvePairingCodeSync, broadcastPairingEvent, buildPairingInstructionText, collectPairingPendingIssues, createStandardPairingAdapter, dismissChannelPairingPending, dismissPairingBySenderIdSync, issuePairingChallenge, listChannelPairingState, listChannelPairingSummary, listPendingPairingRequestsSync, mergeDistinctSenderIds, readAllowFromIdsSync, removeAllowFromIdSync, resolveDefaultCredentialsDir, resolveStandardAllowFromPath, resolveStandardPairingPath, resolveWeixinAllowFromPath, resolveWeixinPairingPath, revokeChannelPairingPaired, setPairingBroadcastSink, upsertPairingRequestSync };
12
+ export { PAIRING_PENDING_MAX, PAIRING_PENDING_TTL_MS, PAIRING_STALE_PENDING_MS, appendAllowFromIdSync, approveChannelPairing, approveChannelPairingBySender, approveChannelPairingFromCli, approvePairingBySenderIdSync, approvePairingCodeSync, broadcastPairingEvent, buildPairingInstructionText, collectPairingPendingIssues, createStandardPairingAdapter, dismissChannelPairingPending, dismissPairingBySenderIdSync, issuePairingChallenge, listChannelPairingState, listChannelPairingSummary, listPendingPairingRequestsSync, mergeDistinctSenderIds, readAllowFromIdsSync, registerPairingChannelResolver, removeAllowFromIdSync, resolveDefaultCredentialsDir, resolveStandardAllowFromPath, resolveStandardPairingPath, resolveWeixinAllowFromPath, resolveWeixinPairingPath, revokeChannelPairingPaired, setPairingBroadcastSink, upsertPairingRequestSync };
@@ -13,6 +13,19 @@ export type ChannelPairingState = {
13
13
  fromCredentials: string[];
14
14
  };
15
15
  };
16
+ export type PairingPaths = {
17
+ pairingPath: string;
18
+ allowPath: string;
19
+ };
20
+ export type PairingChannelConfigResolver = {
21
+ resolvePaths?: (accountId: string) => PairingPaths;
22
+ resolveAllowFromConfig?: (config: Config | undefined, channel: PairingCliChannel, accountId: string) => string[];
23
+ resolveChannelEnabled?: (config: Config | undefined, channel: PairingCliChannel) => boolean;
24
+ resolveAccountEnabled?: (config: Config | undefined, channel: PairingCliChannel, accountId: string) => boolean;
25
+ resolveDmPolicy?: (config: Config | undefined, channel: PairingCliChannel, accountId: string) => DmPolicy;
26
+ resolveAccountIds?: (config: Config | undefined, channel: PairingCliChannel) => string[];
27
+ };
28
+ export declare function registerPairingChannelResolver(channel: PairingCliChannel, resolver: PairingChannelConfigResolver): void;
16
29
  export type ChannelPairingSummaryEntry = {
17
30
  pending: number;
18
31
  stale: number;
@@ -1,24 +1,90 @@
1
1
  import { resolveDmPolicy } from "../security.js";
2
- import { resolveStandardAllowFromPath, resolveStandardPairingPath, resolveWeixinAllowFromPath, resolveWeixinPairingPath } from "./paths.js";
2
+ import { resolveStandardAllowFromPath, resolveStandardPairingPath } from "./paths.js";
3
3
  import { readAllowFromIdsSync, removeAllowFromIdSync } from "./allow-from-file.js";
4
4
  import "./pairing-constants.js";
5
5
  import { PAIRING_PENDING_TTL_MS, approvePairingBySenderIdSync, approvePairingCodeSync, dismissPairingBySenderIdSync, listPendingPairingRequestsSync } from "./pairing-store.js";
6
6
  import { broadcastPairingEvent } from "./pairing-events.js";
7
7
  //#region src/channels/pairing/pairing-service.ts
8
+ const pairingResolvers = /* @__PURE__ */ new Map();
9
+ function registerPairingChannelResolver(channel, resolver) {
10
+ const id = channel.trim().toLowerCase();
11
+ if (!id) return;
12
+ pairingResolvers.set(id, resolver);
13
+ }
8
14
  function normalizeAccountId(accountId) {
9
15
  return (accountId ?? "default").trim().toLowerCase() || "default";
10
16
  }
11
- function resolvePairingPaths(channel, accountId) {
17
+ function channelBlock(config, channel) {
18
+ const ch = config?.channels?.[channel];
19
+ return ch && typeof ch === "object" && !Array.isArray(ch) ? ch : null;
20
+ }
21
+ function channelResolver(channel) {
22
+ return pairingResolvers.get(channel.trim().toLowerCase()) ?? defaultPairingResolver;
23
+ }
24
+ function defaultPairingPaths(channel, accountId) {
12
25
  const normalized = normalizeAccountId(accountId);
13
- if (channel === "weixin") return {
14
- pairingPath: resolveWeixinPairingPath(normalized),
15
- allowPath: resolveWeixinAllowFromPath(normalized)
16
- };
17
26
  return {
18
27
  pairingPath: resolveStandardPairingPath(channel, normalized),
19
28
  allowPath: resolveStandardAllowFromPath(channel, normalized)
20
29
  };
21
30
  }
31
+ function defaultAllowFromConfig(config, channel, accountId) {
32
+ const block = channelBlock(config, channel);
33
+ if (!block) return [];
34
+ const top = Array.isArray(block.allowFrom) ? block.allowFrom.map(String).filter(Boolean) : [];
35
+ const accounts = block.accounts;
36
+ if (accounts && typeof accounts === "object" && !Array.isArray(accounts)) {
37
+ const acc = accounts[accountId];
38
+ if (acc && typeof acc === "object" && !Array.isArray(acc)) {
39
+ const fromAcc = acc.allowFrom;
40
+ if (Array.isArray(fromAcc)) return [...new Set([...top, ...fromAcc.map(String).filter(Boolean)])];
41
+ }
42
+ }
43
+ return [...new Set(top)];
44
+ }
45
+ function defaultChannelEnabled(config, channel) {
46
+ return channelBlock(config, channel)?.enabled === true;
47
+ }
48
+ function defaultAccountEnabled(config, channel, accountId) {
49
+ if (!defaultChannelEnabled(config, channel)) return false;
50
+ const accounts = channelBlock(config, channel)?.accounts;
51
+ if (accounts && typeof accounts === "object" && !Array.isArray(accounts)) {
52
+ const acc = accounts[accountId];
53
+ if (acc && typeof acc === "object" && !Array.isArray(acc)) return acc.enabled !== false;
54
+ }
55
+ return true;
56
+ }
57
+ function defaultDmPolicy(config, channel, accountId) {
58
+ const block = channelBlock(config, channel);
59
+ if (!block) return resolveDmPolicy(void 0, "pairing");
60
+ const accounts = block.accounts;
61
+ if (accounts && typeof accounts === "object" && !Array.isArray(accounts)) {
62
+ const acc = accounts[accountId];
63
+ if (acc && typeof acc === "object" && !Array.isArray(acc)) {
64
+ const accPolicy = acc.dmPolicy;
65
+ if (accPolicy) return resolveDmPolicy(accPolicy, "pairing");
66
+ }
67
+ }
68
+ return resolveDmPolicy(block.dmPolicy, "pairing");
69
+ }
70
+ function defaultAccountIds(config, channel) {
71
+ const accounts = channelBlock(config, channel)?.accounts;
72
+ if (accounts && typeof accounts === "object" && !Array.isArray(accounts)) {
73
+ const keys = Object.keys(accounts).sort();
74
+ if (keys.length > 0) return keys;
75
+ }
76
+ return ["default"];
77
+ }
78
+ const defaultPairingResolver = {
79
+ resolveAllowFromConfig: defaultAllowFromConfig,
80
+ resolveChannelEnabled: defaultChannelEnabled,
81
+ resolveAccountEnabled: defaultAccountEnabled,
82
+ resolveDmPolicy: defaultDmPolicy,
83
+ resolveAccountIds: defaultAccountIds
84
+ };
85
+ function resolvePairingPaths(channel, accountId) {
86
+ return channelResolver(channel).resolvePaths?.(normalizeAccountId(accountId)) ?? defaultPairingPaths(channel, accountId);
87
+ }
22
88
  function requestAccountId(entry) {
23
89
  return entry.meta?.accountId?.trim().toLowerCase() || "default";
24
90
  }
@@ -51,68 +117,17 @@ function pendingEntryStats(pending) {
51
117
  atCapacity: pending.length >= 3
52
118
  };
53
119
  }
54
- function resolveChannelAllowFromConfig(config, channel, accountId) {
55
- const ch = config?.channels?.[channel];
56
- if (!ch || typeof ch !== "object" || Array.isArray(ch)) return [];
57
- const block = ch;
58
- const top = Array.isArray(block.allowFrom) ? block.allowFrom.map(String).filter(Boolean) : [];
59
- const accounts = block.accounts;
60
- if (accounts && typeof accounts === "object" && !Array.isArray(accounts)) {
61
- const acc = accounts[accountId];
62
- if (acc && typeof acc === "object" && !Array.isArray(acc)) {
63
- const fromAcc = acc.allowFrom;
64
- if (Array.isArray(fromAcc)) return [...new Set([...top, ...fromAcc.map(String).filter(Boolean)])];
65
- }
66
- }
67
- return [...new Set(top)];
68
- }
69
120
  function resolveChannelEnabled(config, channel) {
70
- const ch = config?.channels?.[channel];
71
- if (!ch || typeof ch !== "object" || Array.isArray(ch)) return false;
72
- return ch.enabled === true;
121
+ return channelResolver(channel).resolveChannelEnabled?.(config, channel) ?? defaultChannelEnabled(config, channel);
73
122
  }
74
123
  function resolveAccountEnabled(config, channel, accountId) {
75
- if (!resolveChannelEnabled(config, channel)) return false;
76
- const ch = config?.channels?.[channel];
77
- if (!ch || typeof ch !== "object" || Array.isArray(ch)) return true;
78
- const accounts = ch.accounts;
79
- if (accounts && typeof accounts === "object" && !Array.isArray(accounts)) {
80
- const acc = accounts[accountId];
81
- if (acc && typeof acc === "object" && !Array.isArray(acc)) return acc.enabled !== false;
82
- }
83
- return true;
124
+ return channelResolver(channel).resolveAccountEnabled?.(config, channel, accountId) ?? defaultAccountEnabled(config, channel, accountId);
84
125
  }
85
126
  function resolveChannelDmPolicy(config, channel, accountId) {
86
- const ch = config?.channels?.[channel];
87
- if (!ch || typeof ch !== "object" || Array.isArray(ch)) return resolveDmPolicy(void 0, channel === "weixin" ? "open" : "pairing");
88
- const block = ch;
89
- const accounts = block.accounts;
90
- if (accounts && typeof accounts === "object" && !Array.isArray(accounts)) {
91
- const acc = accounts[accountId];
92
- if (acc && typeof acc === "object" && !Array.isArray(acc)) {
93
- const accPolicy = acc.dmPolicy;
94
- if (accPolicy) return resolveDmPolicy(accPolicy, "pairing");
95
- }
96
- }
97
- const topPolicy = block.dmPolicy;
98
- return resolveDmPolicy(topPolicy, channel === "weixin" ? "open" : "pairing");
127
+ return channelResolver(channel).resolveDmPolicy?.(config, channel, accountId) ?? defaultDmPolicy(config, channel, accountId);
99
128
  }
100
129
  function resolvePairingAccountIds(config, channel) {
101
- const ch = config?.channels?.[channel];
102
- if (!ch || typeof ch !== "object" || Array.isArray(ch)) return channel === "telegram" ? ["default"] : [];
103
- const block = ch;
104
- const accounts = block.accounts;
105
- if (accounts && typeof accounts === "object" && !Array.isArray(accounts)) {
106
- const keys = Object.keys(accounts).sort();
107
- if (keys.length > 0) return keys;
108
- }
109
- if (channel === "feishu") {
110
- const appId = typeof block.appId === "string" ? block.appId.trim() : "";
111
- const appSecret = typeof block.appSecret === "string" ? block.appSecret.trim() : "";
112
- return appId && appSecret ? ["default"] : [];
113
- }
114
- if (channel === "telegram") return ["default"];
115
- return ["default"];
130
+ return channelResolver(channel).resolveAccountIds?.(config, channel) ?? defaultAccountIds(config, channel);
116
131
  }
117
132
  function listChannelPairingSummary(config) {
118
133
  const channels = Object.keys(config?.channels ?? {});
@@ -166,7 +181,7 @@ function listChannelPairingState(params) {
166
181
  dmPolicy: resolveChannelDmPolicy(params.config, params.channel, accountId),
167
182
  pending: pendingRaw.map(toPendingView),
168
183
  paired: {
169
- fromConfig: resolveChannelAllowFromConfig(params.config, params.channel, accountId),
184
+ fromConfig: channelResolver(params.channel).resolveAllowFromConfig?.(params.config, params.channel, accountId) ?? defaultAllowFromConfig(params.config, params.channel, accountId),
170
185
  fromCredentials: readAllowFromIdsSync(allowPath)
171
186
  }
172
187
  };
@@ -278,6 +293,6 @@ function dismissChannelPairingPending(params) {
278
293
  };
279
294
  }
280
295
  //#endregion
281
- export { approveChannelPairing, approveChannelPairingBySender, collectPairingPendingIssues, dismissChannelPairingPending, listChannelPairingState, listChannelPairingSummary, revokeChannelPairingPaired };
296
+ export { approveChannelPairing, approveChannelPairingBySender, collectPairingPendingIssues, dismissChannelPairingPending, listChannelPairingState, listChannelPairingSummary, registerPairingChannelResolver, revokeChannelPairingPaired };
282
297
 
283
298
  //# sourceMappingURL=pairing-service.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pairing-service.js","names":["block"],"sources":["../../../../src/channels/pairing/pairing-service.ts"],"sourcesContent":["import type { Config } from '../../config/schema.js';\nimport { resolveDmPolicy } from '../security.js';\nimport type { DmPolicy } from '../channel-domain.js';\n\nimport { readAllowFromIdsSync, removeAllowFromIdSync } from './allow-from-file.js';\nimport type { PairingCliChannel } from './pairing-channel.js';\nimport { broadcastPairingEvent } from './pairing-events.js';\nimport { PAIRING_PENDING_MAX, PAIRING_STALE_PENDING_MS } from './pairing-constants.js';\nimport {\n approvePairingBySenderIdSync,\n approvePairingCodeSync,\n dismissPairingBySenderIdSync,\n listPendingPairingRequestsSync,\n PAIRING_PENDING_TTL_MS,\n type PairingRequest,\n} from './pairing-store.js';\nimport {\n resolveStandardAllowFromPath,\n resolveStandardPairingPath,\n resolveWeixinAllowFromPath,\n resolveWeixinPairingPath,\n} from './paths.js';\n\n// `PairingPendingView` moved to `./pairing-types.js` (leaf) so\n// `plugins/types.adapters.ts` and other lower-level callers can reference it\n// without forming a `types.adapters → pairing-service → security → plugin-types\n// → types.adapters` cycle. Re-exported here for backward-compat.\nexport { type PairingPendingView } from './pairing-types.js';\nimport type { PairingPendingView } from './pairing-types.js';\n\nexport type ChannelPairingState = {\n channel: PairingCliChannel;\n accountId: string;\n dmPolicy: DmPolicy;\n pending: PairingPendingView[];\n paired: {\n fromConfig: string[];\n fromCredentials: string[];\n };\n};\n\nfunction normalizeAccountId(accountId: string | undefined): string {\n return (accountId ?? 'default').trim().toLowerCase() || 'default';\n}\n\nfunction resolvePairingPaths(channel: PairingCliChannel, accountId: string): {\n pairingPath: string;\n allowPath: string;\n} {\n const normalized = normalizeAccountId(accountId);\n if (channel === 'weixin') {\n return {\n pairingPath: resolveWeixinPairingPath(normalized),\n allowPath: resolveWeixinAllowFromPath(normalized),\n };\n }\n return {\n pairingPath: resolveStandardPairingPath(channel, normalized),\n allowPath: resolveStandardAllowFromPath(channel, normalized),\n };\n}\n\nfunction requestAccountId(entry: PairingRequest): string {\n const fromMeta = entry.meta?.accountId?.trim().toLowerCase();\n return fromMeta || 'default';\n}\n\nfunction toPendingView(entry: PairingRequest): PairingPendingView {\n const createdMs = Date.parse(entry.createdAt);\n const expiresAt = Number.isFinite(createdMs)\n ? new Date(createdMs + PAIRING_PENDING_TTL_MS).toISOString()\n : new Date(Date.now() + PAIRING_PENDING_TTL_MS).toISOString();\n const code = entry.code ?? '';\n const codeLast4 = code.length >= 4 ? code.slice(-4) : code;\n const meta =\n entry.meta && typeof entry.meta === 'object'\n ? Object.fromEntries(\n Object.entries(entry.meta).filter(\n ([k, v]) => k !== 'accountId' && typeof v === 'string' && v.trim() !== '',\n ),\n )\n : undefined;\n return {\n senderId: entry.id,\n codeLast4,\n createdAt: entry.createdAt,\n lastSeenAt: entry.lastSeenAt,\n expiresAt,\n isStale:\n Number.isFinite(createdMs) && Date.now() - (createdMs as number) >= PAIRING_STALE_PENDING_MS,\n meta: meta && Object.keys(meta).length > 0 ? meta : undefined,\n };\n}\n\nfunction pendingEntryStats(pending: PairingRequest[]): {\n pending: number;\n stale: number;\n atCapacity: boolean;\n} {\n const now = Date.now();\n let stale = 0;\n for (const p of pending) {\n const created = Date.parse(p.createdAt);\n if (Number.isFinite(created) && now - created >= PAIRING_STALE_PENDING_MS) {\n stale += 1;\n }\n }\n return {\n pending: pending.length,\n stale,\n atCapacity: pending.length >= PAIRING_PENDING_MAX,\n };\n}\n\nfunction resolveChannelAllowFromConfig(\n config: Config | undefined,\n channel: PairingCliChannel,\n accountId: string,\n): string[] {\n const ch = config?.channels?.[channel as keyof NonNullable<Config['channels']>];\n if (!ch || typeof ch !== 'object' || Array.isArray(ch)) return [];\n const block = ch as Record<string, unknown>;\n const top = Array.isArray(block.allowFrom)\n ? block.allowFrom.map(String).filter(Boolean)\n : [];\n const accounts = block.accounts;\n if (accounts && typeof accounts === 'object' && !Array.isArray(accounts)) {\n const acc = (accounts as Record<string, unknown>)[accountId];\n if (acc && typeof acc === 'object' && !Array.isArray(acc)) {\n const fromAcc = (acc as { allowFrom?: unknown }).allowFrom;\n if (Array.isArray(fromAcc)) {\n return [...new Set([...top, ...fromAcc.map(String).filter(Boolean)])];\n }\n }\n }\n return [...new Set(top)];\n}\n\nfunction resolveChannelEnabled(config: Config | undefined, channel: PairingCliChannel): boolean {\n const ch = config?.channels?.[channel as keyof NonNullable<Config['channels']>];\n if (!ch || typeof ch !== 'object' || Array.isArray(ch)) return false;\n return (ch as { enabled?: boolean }).enabled === true;\n}\n\nfunction resolveAccountEnabled(\n config: Config | undefined,\n channel: PairingCliChannel,\n accountId: string,\n): boolean {\n if (!resolveChannelEnabled(config, channel)) return false;\n const ch = config?.channels?.[channel as keyof NonNullable<Config['channels']>];\n if (!ch || typeof ch !== 'object' || Array.isArray(ch)) return true;\n const block = ch as Record<string, unknown>;\n const accounts = block.accounts;\n if (accounts && typeof accounts === 'object' && !Array.isArray(accounts)) {\n const acc = (accounts as Record<string, unknown>)[accountId];\n if (acc && typeof acc === 'object' && !Array.isArray(acc)) {\n return (acc as { enabled?: boolean }).enabled !== false;\n }\n }\n return true;\n}\n\nfunction resolveChannelDmPolicy(\n config: Config | undefined,\n channel: PairingCliChannel,\n accountId: string,\n): DmPolicy {\n const ch = config?.channels?.[channel as keyof NonNullable<Config['channels']>];\n if (!ch || typeof ch !== 'object' || Array.isArray(ch)) {\n return resolveDmPolicy(undefined, channel === 'weixin' ? 'open' : 'pairing');\n }\n const block = ch as Record<string, unknown>;\n const accounts = block.accounts;\n if (accounts && typeof accounts === 'object' && !Array.isArray(accounts)) {\n const acc = (accounts as Record<string, unknown>)[accountId];\n if (acc && typeof acc === 'object' && !Array.isArray(acc)) {\n const accPolicy = (acc as { dmPolicy?: DmPolicy }).dmPolicy;\n if (accPolicy) return resolveDmPolicy(accPolicy, 'pairing');\n }\n }\n const topPolicy = block.dmPolicy as DmPolicy | undefined;\n return resolveDmPolicy(topPolicy, channel === 'weixin' ? 'open' : 'pairing');\n}\n\nexport type ChannelPairingSummaryEntry = {\n pending: number;\n stale: number;\n atCapacity: boolean;\n};\n\nexport type ChannelPairingSummary = Record<string, ChannelPairingSummaryEntry>;\n\nexport type PairingPendingIssue = {\n channel: PairingCliChannel;\n accountId: string;\n pending: number;\n stale: number;\n atCapacity: boolean;\n};\n\nfunction resolvePairingAccountIds(config: Config | undefined, channel: PairingCliChannel): string[] {\n const ch = config?.channels?.[channel as keyof NonNullable<Config['channels']>];\n if (!ch || typeof ch !== 'object' || Array.isArray(ch)) {\n return channel === 'telegram' ? ['default'] : [];\n }\n const block = ch as Record<string, unknown>;\n const accounts = block.accounts;\n if (accounts && typeof accounts === 'object' && !Array.isArray(accounts)) {\n const keys = Object.keys(accounts as Record<string, unknown>).sort();\n if (keys.length > 0) return keys;\n }\n if (channel === 'feishu') {\n const appId = typeof block.appId === 'string' ? block.appId.trim() : '';\n const appSecret = typeof block.appSecret === 'string' ? block.appSecret.trim() : '';\n return appId && appSecret ? ['default'] : [];\n }\n if (channel === 'telegram') return ['default'];\n return ['default'];\n}\n\nexport function listChannelPairingSummary(config?: Config): ChannelPairingSummary {\n const channels = Object.keys(config?.channels ?? {});\n const summary: ChannelPairingSummary = {};\n for (const channel of channels) {\n if (!resolveChannelEnabled(config, channel)) continue;\n summary[channel] = { pending: 0, stale: 0, atCapacity: false };\n const accountIds = resolvePairingAccountIds(config, channel);\n for (const accountId of accountIds) {\n if (!resolveAccountEnabled(config, channel, accountId)) continue;\n if (resolveChannelDmPolicy(config, channel, accountId) !== 'pairing') continue;\n const { pairingPath } = resolvePairingPaths(channel, accountId);\n const pendingRaw = listPendingPairingRequestsSync(pairingPath).filter(\n (r) => requestAccountId(r) === accountId,\n );\n const stats = pendingEntryStats(pendingRaw);\n summary[channel]!.pending += stats.pending;\n summary[channel]!.stale += stats.stale;\n summary[channel]!.atCapacity ||= stats.atCapacity;\n }\n }\n return summary;\n}\n\nexport function collectPairingPendingIssues(config?: Config): PairingPendingIssue[] {\n const issues: PairingPendingIssue[] = [];\n for (const channel of Object.keys(config?.channels ?? {})) {\n if (!resolveChannelEnabled(config, channel)) continue;\n const accountIds = resolvePairingAccountIds(config, channel);\n for (const accountId of accountIds) {\n if (!resolveAccountEnabled(config, channel, accountId)) continue;\n if (resolveChannelDmPolicy(config, channel, accountId) !== 'pairing') continue;\n const { pairingPath } = resolvePairingPaths(channel, accountId);\n const pendingRaw = listPendingPairingRequestsSync(pairingPath).filter(\n (r) => requestAccountId(r) === accountId,\n );\n const stats = pendingEntryStats(pendingRaw);\n if (stats.pending > 0) {\n issues.push({ channel, accountId, ...stats });\n }\n }\n }\n return issues;\n}\n\nexport function listChannelPairingState(params: {\n channel: PairingCliChannel;\n accountId?: string;\n config?: Config;\n}): ChannelPairingState {\n const accountId = normalizeAccountId(params.accountId);\n const { pairingPath, allowPath } = resolvePairingPaths(params.channel, accountId);\n const pendingRaw = listPendingPairingRequestsSync(pairingPath).filter(\n (r) => requestAccountId(r) === accountId,\n );\n return {\n channel: params.channel,\n accountId,\n dmPolicy: resolveChannelDmPolicy(params.config, params.channel, accountId),\n pending: pendingRaw.map(toPendingView),\n paired: {\n fromConfig: resolveChannelAllowFromConfig(params.config, params.channel, accountId),\n fromCredentials: readAllowFromIdsSync(allowPath),\n },\n };\n}\n\nexport function approveChannelPairing(params: {\n channel: PairingCliChannel;\n accountId?: string;\n code: string;\n}): { ok: true; senderId: string; alreadyPaired: boolean } | { ok: false; error: string } {\n const accountId = normalizeAccountId(params.accountId);\n const code = params.code.trim();\n if (!code) return { ok: false, error: 'Missing pairing code.' };\n\n const { pairingPath, allowPath } = resolvePairingPaths(params.channel, accountId);\n const alreadyPaired = readAllowFromIdsSync(allowPath);\n\n const result = approvePairingCodeSync({\n pairingFilePath: pairingPath,\n allowFromFilePath: allowPath,\n code,\n accountId,\n });\n if (!result) {\n return { ok: false, error: 'Invalid or expired pairing code (or wrong account).' };\n }\n broadcastPairingEvent('channels.pairing.approved', {\n channel: params.channel,\n accountId,\n senderId: result.senderId,\n });\n return {\n ok: true,\n senderId: result.senderId,\n alreadyPaired: alreadyPaired.includes(result.senderId),\n };\n}\n\nexport function approveChannelPairingBySender(params: {\n channel: PairingCliChannel;\n accountId?: string;\n senderId: string;\n}): { ok: true; senderId: string; alreadyPaired: boolean } | { ok: false; error: string } {\n const accountId = normalizeAccountId(params.accountId);\n const senderId = params.senderId.trim();\n if (!senderId) return { ok: false, error: 'Missing sender id.' };\n\n const { pairingPath, allowPath } = resolvePairingPaths(params.channel, accountId);\n const alreadyPaired = readAllowFromIdsSync(allowPath);\n\n const result = approvePairingBySenderIdSync({\n pairingFilePath: pairingPath,\n allowFromFilePath: allowPath,\n senderId,\n accountId,\n });\n if (!result) {\n return { ok: false, error: 'No pending pairing request for this sender (or wrong account).' };\n }\n broadcastPairingEvent('channels.pairing.approved', {\n channel: params.channel,\n accountId,\n senderId: result.senderId,\n });\n return {\n ok: true,\n senderId: result.senderId,\n alreadyPaired: alreadyPaired.includes(result.senderId),\n };\n}\n\nexport function revokeChannelPairingPaired(params: {\n channel: PairingCliChannel;\n accountId?: string;\n senderId: string;\n}): { ok: true; changed: boolean } | { ok: false; error: string } {\n const accountId = normalizeAccountId(params.accountId);\n const senderId = params.senderId.trim();\n if (!senderId) return { ok: false, error: 'Missing sender id.' };\n\n const { allowPath } = resolvePairingPaths(params.channel, accountId);\n const { changed } = removeAllowFromIdSync(allowPath, senderId);\n if (changed) {\n broadcastPairingEvent('channels.pairing.revoked', {\n channel: params.channel,\n accountId,\n senderId,\n });\n }\n return { ok: true, changed };\n}\n\nexport function dismissChannelPairingPending(params: {\n channel: PairingCliChannel;\n accountId?: string;\n senderId: string;\n}): { ok: true; senderId: string } | { ok: false; error: string } {\n const accountId = normalizeAccountId(params.accountId);\n const senderId = params.senderId.trim();\n if (!senderId) return { ok: false, error: 'Missing sender id.' };\n\n const { pairingPath } = resolvePairingPaths(params.channel, accountId);\n const result = dismissPairingBySenderIdSync({\n pairingFilePath: pairingPath,\n senderId,\n accountId,\n });\n if (!result) {\n return { ok: false, error: 'No pending pairing request for this sender (or wrong account).' };\n }\n broadcastPairingEvent('channels.pairing.dismissed', {\n channel: params.channel,\n accountId,\n senderId: result.senderId,\n });\n return { ok: true, senderId: result.senderId };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pairing-service.js","names":[],"sources":["../../../../src/channels/pairing/pairing-service.ts"],"sourcesContent":["import type { Config } from '../../config/schema.js';\nimport { resolveDmPolicy } from '../security.js';\nimport type { DmPolicy } from '../channel-domain.js';\n\nimport { readAllowFromIdsSync, removeAllowFromIdSync } from './allow-from-file.js';\nimport type { PairingCliChannel } from './pairing-channel.js';\nimport { broadcastPairingEvent } from './pairing-events.js';\nimport { PAIRING_PENDING_MAX, PAIRING_STALE_PENDING_MS } from './pairing-constants.js';\nimport {\n approvePairingBySenderIdSync,\n approvePairingCodeSync,\n dismissPairingBySenderIdSync,\n listPendingPairingRequestsSync,\n PAIRING_PENDING_TTL_MS,\n type PairingRequest,\n} from './pairing-store.js';\nimport {\n resolveStandardAllowFromPath,\n resolveStandardPairingPath,\n} from './paths.js';\n\n// `PairingPendingView` moved to `./pairing-types.js` (leaf) so\n// `plugins/types.adapters.ts` and other lower-level callers can reference it\n// without forming a `types.adapters → pairing-service → security → plugin-types\n// → types.adapters` cycle. Re-exported here for backward-compat.\nexport { type PairingPendingView } from './pairing-types.js';\nimport type { PairingPendingView } from './pairing-types.js';\n\nexport type ChannelPairingState = {\n channel: PairingCliChannel;\n accountId: string;\n dmPolicy: DmPolicy;\n pending: PairingPendingView[];\n paired: {\n fromConfig: string[];\n fromCredentials: string[];\n };\n};\n\nexport type PairingPaths = {\n pairingPath: string;\n allowPath: string;\n};\n\nexport type PairingChannelConfigResolver = {\n resolvePaths?: (accountId: string) => PairingPaths;\n resolveAllowFromConfig?: (config: Config | undefined, channel: PairingCliChannel, accountId: string) => string[];\n resolveChannelEnabled?: (config: Config | undefined, channel: PairingCliChannel) => boolean;\n resolveAccountEnabled?: (config: Config | undefined, channel: PairingCliChannel, accountId: string) => boolean;\n resolveDmPolicy?: (config: Config | undefined, channel: PairingCliChannel, accountId: string) => DmPolicy;\n resolveAccountIds?: (config: Config | undefined, channel: PairingCliChannel) => string[];\n};\n\nconst pairingResolvers = new Map<string, PairingChannelConfigResolver>();\n\nexport function registerPairingChannelResolver(\n channel: PairingCliChannel,\n resolver: PairingChannelConfigResolver,\n): void {\n const id = channel.trim().toLowerCase();\n if (!id) return;\n pairingResolvers.set(id, resolver);\n}\n\nfunction normalizeAccountId(accountId: string | undefined): string {\n return (accountId ?? 'default').trim().toLowerCase() || 'default';\n}\n\nfunction channelBlock(config: Config | undefined, channel: PairingCliChannel): Record<string, unknown> | null {\n const ch = config?.channels?.[channel as keyof NonNullable<Config['channels']>];\n return ch && typeof ch === 'object' && !Array.isArray(ch) ? (ch as Record<string, unknown>) : null;\n}\n\nfunction channelResolver(channel: PairingCliChannel): PairingChannelConfigResolver {\n return pairingResolvers.get(channel.trim().toLowerCase()) ?? defaultPairingResolver;\n}\n\nfunction defaultPairingPaths(channel: PairingCliChannel, accountId: string): PairingPaths {\n const normalized = normalizeAccountId(accountId);\n return {\n pairingPath: resolveStandardPairingPath(channel, normalized),\n allowPath: resolveStandardAllowFromPath(channel, normalized),\n };\n}\n\nfunction defaultAllowFromConfig(\n config: Config | undefined,\n channel: PairingCliChannel,\n accountId: string,\n): string[] {\n const block = channelBlock(config, channel);\n if (!block) return [];\n const top = Array.isArray(block.allowFrom)\n ? block.allowFrom.map(String).filter(Boolean)\n : [];\n const accounts = block.accounts;\n if (accounts && typeof accounts === 'object' && !Array.isArray(accounts)) {\n const acc = (accounts as Record<string, unknown>)[accountId];\n if (acc && typeof acc === 'object' && !Array.isArray(acc)) {\n const fromAcc = (acc as { allowFrom?: unknown }).allowFrom;\n if (Array.isArray(fromAcc)) {\n return [...new Set([...top, ...fromAcc.map(String).filter(Boolean)])];\n }\n }\n }\n return [...new Set(top)];\n}\n\nfunction defaultChannelEnabled(config: Config | undefined, channel: PairingCliChannel): boolean {\n return channelBlock(config, channel)?.enabled === true;\n}\n\nfunction defaultAccountEnabled(\n config: Config | undefined,\n channel: PairingCliChannel,\n accountId: string,\n): boolean {\n if (!defaultChannelEnabled(config, channel)) return false;\n const accounts = channelBlock(config, channel)?.accounts;\n if (accounts && typeof accounts === 'object' && !Array.isArray(accounts)) {\n const acc = (accounts as Record<string, unknown>)[accountId];\n if (acc && typeof acc === 'object' && !Array.isArray(acc)) {\n return (acc as { enabled?: boolean }).enabled !== false;\n }\n }\n return true;\n}\n\nfunction defaultDmPolicy(config: Config | undefined, channel: PairingCliChannel, accountId: string): DmPolicy {\n const block = channelBlock(config, channel);\n if (!block) return resolveDmPolicy(undefined, 'pairing');\n const accounts = block.accounts;\n if (accounts && typeof accounts === 'object' && !Array.isArray(accounts)) {\n const acc = (accounts as Record<string, unknown>)[accountId];\n if (acc && typeof acc === 'object' && !Array.isArray(acc)) {\n const accPolicy = (acc as { dmPolicy?: DmPolicy }).dmPolicy;\n if (accPolicy) return resolveDmPolicy(accPolicy, 'pairing');\n }\n }\n return resolveDmPolicy(block.dmPolicy as DmPolicy | undefined, 'pairing');\n}\n\nfunction defaultAccountIds(config: Config | undefined, channel: PairingCliChannel): string[] {\n const accounts = channelBlock(config, channel)?.accounts;\n if (accounts && typeof accounts === 'object' && !Array.isArray(accounts)) {\n const keys = Object.keys(accounts as Record<string, unknown>).sort();\n if (keys.length > 0) return keys;\n }\n return ['default'];\n}\n\nconst defaultPairingResolver: PairingChannelConfigResolver = {\n resolveAllowFromConfig: defaultAllowFromConfig,\n resolveChannelEnabled: defaultChannelEnabled,\n resolveAccountEnabled: defaultAccountEnabled,\n resolveDmPolicy: defaultDmPolicy,\n resolveAccountIds: defaultAccountIds,\n};\n\nfunction resolvePairingPaths(channel: PairingCliChannel, accountId: string): PairingPaths {\n return channelResolver(channel).resolvePaths?.(normalizeAccountId(accountId)) ??\n defaultPairingPaths(channel, accountId);\n}\n\nfunction requestAccountId(entry: PairingRequest): string {\n const fromMeta = entry.meta?.accountId?.trim().toLowerCase();\n return fromMeta || 'default';\n}\n\nfunction toPendingView(entry: PairingRequest): PairingPendingView {\n const createdMs = Date.parse(entry.createdAt);\n const expiresAt = Number.isFinite(createdMs)\n ? new Date(createdMs + PAIRING_PENDING_TTL_MS).toISOString()\n : new Date(Date.now() + PAIRING_PENDING_TTL_MS).toISOString();\n const code = entry.code ?? '';\n const codeLast4 = code.length >= 4 ? code.slice(-4) : code;\n const meta =\n entry.meta && typeof entry.meta === 'object'\n ? Object.fromEntries(\n Object.entries(entry.meta).filter(\n ([k, v]) => k !== 'accountId' && typeof v === 'string' && v.trim() !== '',\n ),\n )\n : undefined;\n return {\n senderId: entry.id,\n codeLast4,\n createdAt: entry.createdAt,\n lastSeenAt: entry.lastSeenAt,\n expiresAt,\n isStale:\n Number.isFinite(createdMs) && Date.now() - (createdMs as number) >= PAIRING_STALE_PENDING_MS,\n meta: meta && Object.keys(meta).length > 0 ? meta : undefined,\n };\n}\n\nfunction pendingEntryStats(pending: PairingRequest[]): {\n pending: number;\n stale: number;\n atCapacity: boolean;\n} {\n const now = Date.now();\n let stale = 0;\n for (const p of pending) {\n const created = Date.parse(p.createdAt);\n if (Number.isFinite(created) && now - created >= PAIRING_STALE_PENDING_MS) {\n stale += 1;\n }\n }\n return {\n pending: pending.length,\n stale,\n atCapacity: pending.length >= PAIRING_PENDING_MAX,\n };\n}\n\nfunction resolveChannelEnabled(config: Config | undefined, channel: PairingCliChannel): boolean {\n return channelResolver(channel).resolveChannelEnabled?.(config, channel) ??\n defaultChannelEnabled(config, channel);\n}\n\nfunction resolveAccountEnabled(\n config: Config | undefined,\n channel: PairingCliChannel,\n accountId: string,\n): boolean {\n return channelResolver(channel).resolveAccountEnabled?.(config, channel, accountId) ??\n defaultAccountEnabled(config, channel, accountId);\n}\n\nfunction resolveChannelDmPolicy(\n config: Config | undefined,\n channel: PairingCliChannel,\n accountId: string,\n): DmPolicy {\n return channelResolver(channel).resolveDmPolicy?.(config, channel, accountId) ??\n defaultDmPolicy(config, channel, accountId);\n}\n\nexport type ChannelPairingSummaryEntry = {\n pending: number;\n stale: number;\n atCapacity: boolean;\n};\n\nexport type ChannelPairingSummary = Record<string, ChannelPairingSummaryEntry>;\n\nexport type PairingPendingIssue = {\n channel: PairingCliChannel;\n accountId: string;\n pending: number;\n stale: number;\n atCapacity: boolean;\n};\n\nfunction resolvePairingAccountIds(config: Config | undefined, channel: PairingCliChannel): string[] {\n return channelResolver(channel).resolveAccountIds?.(config, channel) ??\n defaultAccountIds(config, channel);\n}\n\nexport function listChannelPairingSummary(config?: Config): ChannelPairingSummary {\n const channels = Object.keys(config?.channels ?? {});\n const summary: ChannelPairingSummary = {};\n for (const channel of channels) {\n if (!resolveChannelEnabled(config, channel)) continue;\n summary[channel] = { pending: 0, stale: 0, atCapacity: false };\n const accountIds = resolvePairingAccountIds(config, channel);\n for (const accountId of accountIds) {\n if (!resolveAccountEnabled(config, channel, accountId)) continue;\n if (resolveChannelDmPolicy(config, channel, accountId) !== 'pairing') continue;\n const { pairingPath } = resolvePairingPaths(channel, accountId);\n const pendingRaw = listPendingPairingRequestsSync(pairingPath).filter(\n (r) => requestAccountId(r) === accountId,\n );\n const stats = pendingEntryStats(pendingRaw);\n summary[channel]!.pending += stats.pending;\n summary[channel]!.stale += stats.stale;\n summary[channel]!.atCapacity ||= stats.atCapacity;\n }\n }\n return summary;\n}\n\nexport function collectPairingPendingIssues(config?: Config): PairingPendingIssue[] {\n const issues: PairingPendingIssue[] = [];\n for (const channel of Object.keys(config?.channels ?? {})) {\n if (!resolveChannelEnabled(config, channel)) continue;\n const accountIds = resolvePairingAccountIds(config, channel);\n for (const accountId of accountIds) {\n if (!resolveAccountEnabled(config, channel, accountId)) continue;\n if (resolveChannelDmPolicy(config, channel, accountId) !== 'pairing') continue;\n const { pairingPath } = resolvePairingPaths(channel, accountId);\n const pendingRaw = listPendingPairingRequestsSync(pairingPath).filter(\n (r) => requestAccountId(r) === accountId,\n );\n const stats = pendingEntryStats(pendingRaw);\n if (stats.pending > 0) {\n issues.push({ channel, accountId, ...stats });\n }\n }\n }\n return issues;\n}\n\nexport function listChannelPairingState(params: {\n channel: PairingCliChannel;\n accountId?: string;\n config?: Config;\n}): ChannelPairingState {\n const accountId = normalizeAccountId(params.accountId);\n const { pairingPath, allowPath } = resolvePairingPaths(params.channel, accountId);\n const pendingRaw = listPendingPairingRequestsSync(pairingPath).filter(\n (r) => requestAccountId(r) === accountId,\n );\n return {\n channel: params.channel,\n accountId,\n dmPolicy: resolveChannelDmPolicy(params.config, params.channel, accountId),\n pending: pendingRaw.map(toPendingView),\n paired: {\n fromConfig:\n channelResolver(params.channel).resolveAllowFromConfig?.(params.config, params.channel, accountId) ??\n defaultAllowFromConfig(params.config, params.channel, accountId),\n fromCredentials: readAllowFromIdsSync(allowPath),\n },\n };\n}\n\nexport function approveChannelPairing(params: {\n channel: PairingCliChannel;\n accountId?: string;\n code: string;\n}): { ok: true; senderId: string; alreadyPaired: boolean } | { ok: false; error: string } {\n const accountId = normalizeAccountId(params.accountId);\n const code = params.code.trim();\n if (!code) return { ok: false, error: 'Missing pairing code.' };\n\n const { pairingPath, allowPath } = resolvePairingPaths(params.channel, accountId);\n const alreadyPaired = readAllowFromIdsSync(allowPath);\n\n const result = approvePairingCodeSync({\n pairingFilePath: pairingPath,\n allowFromFilePath: allowPath,\n code,\n accountId,\n });\n if (!result) {\n return { ok: false, error: 'Invalid or expired pairing code (or wrong account).' };\n }\n broadcastPairingEvent('channels.pairing.approved', {\n channel: params.channel,\n accountId,\n senderId: result.senderId,\n });\n return {\n ok: true,\n senderId: result.senderId,\n alreadyPaired: alreadyPaired.includes(result.senderId),\n };\n}\n\nexport function approveChannelPairingBySender(params: {\n channel: PairingCliChannel;\n accountId?: string;\n senderId: string;\n}): { ok: true; senderId: string; alreadyPaired: boolean } | { ok: false; error: string } {\n const accountId = normalizeAccountId(params.accountId);\n const senderId = params.senderId.trim();\n if (!senderId) return { ok: false, error: 'Missing sender id.' };\n\n const { pairingPath, allowPath } = resolvePairingPaths(params.channel, accountId);\n const alreadyPaired = readAllowFromIdsSync(allowPath);\n\n const result = approvePairingBySenderIdSync({\n pairingFilePath: pairingPath,\n allowFromFilePath: allowPath,\n senderId,\n accountId,\n });\n if (!result) {\n return { ok: false, error: 'No pending pairing request for this sender (or wrong account).' };\n }\n broadcastPairingEvent('channels.pairing.approved', {\n channel: params.channel,\n accountId,\n senderId: result.senderId,\n });\n return {\n ok: true,\n senderId: result.senderId,\n alreadyPaired: alreadyPaired.includes(result.senderId),\n };\n}\n\nexport function revokeChannelPairingPaired(params: {\n channel: PairingCliChannel;\n accountId?: string;\n senderId: string;\n}): { ok: true; changed: boolean } | { ok: false; error: string } {\n const accountId = normalizeAccountId(params.accountId);\n const senderId = params.senderId.trim();\n if (!senderId) return { ok: false, error: 'Missing sender id.' };\n\n const { allowPath } = resolvePairingPaths(params.channel, accountId);\n const { changed } = removeAllowFromIdSync(allowPath, senderId);\n if (changed) {\n broadcastPairingEvent('channels.pairing.revoked', {\n channel: params.channel,\n accountId,\n senderId,\n });\n }\n return { ok: true, changed };\n}\n\nexport function dismissChannelPairingPending(params: {\n channel: PairingCliChannel;\n accountId?: string;\n senderId: string;\n}): { ok: true; senderId: string } | { ok: false; error: string } {\n const accountId = normalizeAccountId(params.accountId);\n const senderId = params.senderId.trim();\n if (!senderId) return { ok: false, error: 'Missing sender id.' };\n\n const { pairingPath } = resolvePairingPaths(params.channel, accountId);\n const result = dismissPairingBySenderIdSync({\n pairingFilePath: pairingPath,\n senderId,\n accountId,\n });\n if (!result) {\n return { ok: false, error: 'No pending pairing request for this sender (or wrong account).' };\n }\n broadcastPairingEvent('channels.pairing.dismissed', {\n channel: params.channel,\n accountId,\n senderId: result.senderId,\n });\n return { ok: true, senderId: result.senderId };\n}\n"],"mappings":"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"}
@@ -51,17 +51,10 @@ function senderMayWritePersistentConfig(ctx) {
51
51
  const tg = cfg.channels?.telegram;
52
52
  if (!tg?.enabled) return false;
53
53
  const accountId = parseSessionKey(sessionKey)?.accountId ?? "default";
54
- let allowFrom = [];
55
- let groupAllowFrom = [];
56
- if (tg.accounts && Object.keys(tg.accounts).length > 0) {
57
- const acc = tg.accounts[accountId] ?? tg.accounts["default"];
58
- if (!acc || acc.enabled === false) return false;
59
- allowFrom = [...acc.allowFrom ?? []];
60
- groupAllowFrom = [...acc.groupAllowFrom ?? []];
61
- } else {
62
- allowFrom = [...tg.allowFrom ?? []];
63
- groupAllowFrom = [...tg.groupAllowFrom ?? []];
64
- }
54
+ const acc = tg.accounts?.[accountId] ?? tg.accounts?.["default"];
55
+ if (!acc || acc.enabled === false) return false;
56
+ const allowFrom = [...acc.allowFrom ?? []];
57
+ const groupAllowFrom = [...acc.groupAllowFrom ?? []];
65
58
  const list = isGroup && groupAllowFrom.length > 0 ? groupAllowFrom : allowFrom;
66
59
  if (list.length === 0) return false;
67
60
  return resolveAllowlistMatchSimple({