@0xmaxma/claude-gateway 1.8.13 → 2.0.0

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 (594) hide show
  1. package/README.md +28 -1605
  2. package/config.template.json +7 -1
  3. package/dist/agent/runner.d.ts +394 -0
  4. package/dist/agent/runner.d.ts.map +1 -1
  5. package/dist/agent/runner.js +867 -38
  6. package/dist/agent/runner.js.map +1 -1
  7. package/dist/agent/skill-learning/index.d.ts +6 -4
  8. package/dist/agent/skill-learning/index.d.ts.map +1 -1
  9. package/dist/agent/skill-learning/index.js +3 -3
  10. package/dist/agent/skill-learning/index.js.map +1 -1
  11. package/dist/agent/skill-learning/notifier.d.ts +3 -4
  12. package/dist/agent/skill-learning/notifier.d.ts.map +1 -1
  13. package/dist/agent/skill-learning/notifier.js +15 -13
  14. package/dist/agent/skill-learning/notifier.js.map +1 -1
  15. package/dist/agent/skill-learning/reviewer.d.ts.map +1 -1
  16. package/dist/agent/skill-learning/reviewer.js +3 -1
  17. package/dist/agent/skill-learning/reviewer.js.map +1 -1
  18. package/dist/api/connectors-router.d.ts.map +1 -1
  19. package/dist/api/connectors-router.js +110 -4
  20. package/dist/api/connectors-router.js.map +1 -1
  21. package/dist/api/gateway-router.d.ts +1 -0
  22. package/dist/api/gateway-router.d.ts.map +1 -1
  23. package/dist/api/gateway-router.js +13 -1
  24. package/dist/api/gateway-router.js.map +1 -1
  25. package/dist/api/line-webhook-router.d.ts +2 -2
  26. package/dist/api/line-webhook-router.d.ts.map +1 -1
  27. package/dist/api/line-webhook-router.js +264 -230
  28. package/dist/api/line-webhook-router.js.map +1 -1
  29. package/dist/api/router.d.ts.map +1 -1
  30. package/dist/api/router.js +372 -11
  31. package/dist/api/router.js.map +1 -1
  32. package/dist/api/share-router.d.ts.map +1 -1
  33. package/dist/api/share-router.js +29 -10
  34. package/dist/api/share-router.js.map +1 -1
  35. package/dist/api/slack-client.d.ts +2 -0
  36. package/dist/api/slack-client.d.ts.map +1 -1
  37. package/dist/api/slack-client.js +6 -2
  38. package/dist/api/slack-client.js.map +1 -1
  39. package/dist/api/slack-webhook-router.d.ts +3 -0
  40. package/dist/api/slack-webhook-router.d.ts.map +1 -1
  41. package/dist/api/slack-webhook-router.js +226 -131
  42. package/dist/api/slack-webhook-router.js.map +1 -1
  43. package/dist/api/voice-router.d.ts +21 -0
  44. package/dist/api/voice-router.d.ts.map +1 -0
  45. package/dist/api/voice-router.js +372 -0
  46. package/dist/api/voice-router.js.map +1 -0
  47. package/dist/api/voice-settings-router.d.ts +5 -0
  48. package/dist/api/voice-settings-router.d.ts.map +1 -0
  49. package/dist/api/voice-settings-router.js +318 -0
  50. package/dist/api/voice-settings-router.js.map +1 -0
  51. package/dist/api/wechat-access.d.ts +45 -0
  52. package/dist/api/wechat-access.d.ts.map +1 -0
  53. package/dist/api/wechat-access.js +48 -0
  54. package/dist/api/wechat-access.js.map +1 -0
  55. package/dist/apps/agent-container-migration.d.ts +11 -0
  56. package/dist/apps/agent-container-migration.d.ts.map +1 -0
  57. package/dist/apps/agent-container-migration.js +85 -0
  58. package/dist/apps/agent-container-migration.js.map +1 -0
  59. package/dist/apps/installer.d.ts +1 -1
  60. package/dist/apps/installer.d.ts.map +1 -1
  61. package/dist/apps/installer.js +2 -1
  62. package/dist/apps/installer.js.map +1 -1
  63. package/dist/cli/commands/tasks.d.ts +5 -0
  64. package/dist/cli/commands/tasks.d.ts.map +1 -0
  65. package/dist/cli/commands/tasks.js +92 -0
  66. package/dist/cli/commands/tasks.js.map +1 -0
  67. package/dist/cli/http-client.d.ts +2 -0
  68. package/dist/cli/http-client.d.ts.map +1 -1
  69. package/dist/cli/http-client.js +8 -3
  70. package/dist/cli/http-client.js.map +1 -1
  71. package/dist/cli/index.d.ts.map +1 -1
  72. package/dist/cli/index.js +5 -1
  73. package/dist/cli/index.js.map +1 -1
  74. package/dist/config/config-write-lock.d.ts +2 -0
  75. package/dist/config/config-write-lock.d.ts.map +1 -1
  76. package/dist/config/config-write-lock.js +5 -0
  77. package/dist/config/config-write-lock.js.map +1 -1
  78. package/dist/config/loader.d.ts.map +1 -1
  79. package/dist/config/loader.js +87 -2
  80. package/dist/config/loader.js.map +1 -1
  81. package/dist/config/migrator.d.ts.map +1 -1
  82. package/dist/config/migrator.js +12 -1
  83. package/dist/config/migrator.js.map +1 -1
  84. package/dist/config/watcher.d.ts.map +1 -1
  85. package/dist/config/watcher.js +6 -0
  86. package/dist/config/watcher.js.map +1 -1
  87. package/dist/connectors/resolve.d.ts.map +1 -1
  88. package/dist/connectors/resolve.js +3 -1
  89. package/dist/connectors/resolve.js.map +1 -1
  90. package/dist/connectors/types.d.ts +8 -0
  91. package/dist/connectors/types.d.ts.map +1 -1
  92. package/dist/cron/manager.d.ts.map +1 -1
  93. package/dist/cron/manager.js +21 -2
  94. package/dist/cron/manager.js.map +1 -1
  95. package/dist/discord/receiver.d.ts +2 -1
  96. package/dist/discord/receiver.d.ts.map +1 -1
  97. package/dist/discord/receiver.js +5 -1
  98. package/dist/discord/receiver.js.map +1 -1
  99. package/dist/history/db.d.ts +3 -0
  100. package/dist/history/db.d.ts.map +1 -1
  101. package/dist/history/db.js +21 -1
  102. package/dist/history/db.js.map +1 -1
  103. package/dist/history/types.d.ts +12 -1
  104. package/dist/history/types.d.ts.map +1 -1
  105. package/dist/history/types.js +10 -1
  106. package/dist/history/types.js.map +1 -1
  107. package/dist/index.js +48 -9
  108. package/dist/index.js.map +1 -1
  109. package/dist/orchestration/bounded-queue.d.ts +15 -0
  110. package/dist/orchestration/bounded-queue.d.ts.map +1 -0
  111. package/dist/orchestration/bounded-queue.js +58 -0
  112. package/dist/orchestration/bounded-queue.js.map +1 -0
  113. package/dist/orchestration/bridge.d.ts +55 -0
  114. package/dist/orchestration/bridge.d.ts.map +1 -0
  115. package/dist/orchestration/bridge.js +234 -0
  116. package/dist/orchestration/bridge.js.map +1 -0
  117. package/dist/orchestration/browser-routing.d.ts +7 -0
  118. package/dist/orchestration/browser-routing.d.ts.map +1 -0
  119. package/dist/orchestration/browser-routing.js +22 -0
  120. package/dist/orchestration/browser-routing.js.map +1 -0
  121. package/dist/orchestration/capabilities.d.ts +49 -0
  122. package/dist/orchestration/capabilities.d.ts.map +1 -0
  123. package/dist/orchestration/capabilities.js +417 -0
  124. package/dist/orchestration/capabilities.js.map +1 -0
  125. package/dist/orchestration/capacity.d.ts +14 -0
  126. package/dist/orchestration/capacity.d.ts.map +1 -0
  127. package/dist/orchestration/capacity.js +52 -0
  128. package/dist/orchestration/capacity.js.map +1 -0
  129. package/dist/orchestration/channel-activity.d.ts +21 -0
  130. package/dist/orchestration/channel-activity.d.ts.map +1 -0
  131. package/dist/orchestration/channel-activity.js +72 -0
  132. package/dist/orchestration/channel-activity.js.map +1 -0
  133. package/dist/orchestration/channel-controls.d.ts +32 -0
  134. package/dist/orchestration/channel-controls.d.ts.map +1 -0
  135. package/dist/orchestration/channel-controls.js +105 -0
  136. package/dist/orchestration/channel-controls.js.map +1 -0
  137. package/dist/orchestration/channel-input-media.d.ts +11 -0
  138. package/dist/orchestration/channel-input-media.d.ts.map +1 -0
  139. package/dist/orchestration/channel-input-media.js +54 -0
  140. package/dist/orchestration/channel-input-media.js.map +1 -0
  141. package/dist/orchestration/channel-media.d.ts +4 -0
  142. package/dist/orchestration/channel-media.d.ts.map +1 -0
  143. package/dist/orchestration/channel-media.js +70 -0
  144. package/dist/orchestration/channel-media.js.map +1 -0
  145. package/dist/orchestration/channel-speech.d.ts +7 -0
  146. package/dist/orchestration/channel-speech.d.ts.map +1 -0
  147. package/dist/orchestration/channel-speech.js +65 -0
  148. package/dist/orchestration/channel-speech.js.map +1 -0
  149. package/dist/orchestration/cli-skills.d.ts +12 -0
  150. package/dist/orchestration/cli-skills.d.ts.map +1 -0
  151. package/dist/orchestration/cli-skills.js +132 -0
  152. package/dist/orchestration/cli-skills.js.map +1 -0
  153. package/dist/orchestration/config.d.ts +256 -0
  154. package/dist/orchestration/config.d.ts.map +1 -0
  155. package/dist/orchestration/config.js +110 -0
  156. package/dist/orchestration/config.js.map +1 -0
  157. package/dist/orchestration/container.d.ts +21 -0
  158. package/dist/orchestration/container.d.ts.map +1 -0
  159. package/dist/orchestration/container.js +195 -0
  160. package/dist/orchestration/container.js.map +1 -0
  161. package/dist/orchestration/control-delivery.d.ts +5 -0
  162. package/dist/orchestration/control-delivery.d.ts.map +1 -0
  163. package/dist/orchestration/control-delivery.js +50 -0
  164. package/dist/orchestration/control-delivery.js.map +1 -0
  165. package/dist/orchestration/conversation-intake.d.ts +32 -0
  166. package/dist/orchestration/conversation-intake.d.ts.map +1 -0
  167. package/dist/orchestration/conversation-intake.js +92 -0
  168. package/dist/orchestration/conversation-intake.js.map +1 -0
  169. package/dist/orchestration/decisions.d.ts +27 -0
  170. package/dist/orchestration/decisions.d.ts.map +1 -0
  171. package/dist/orchestration/decisions.js +148 -0
  172. package/dist/orchestration/decisions.js.map +1 -0
  173. package/dist/orchestration/delivery.d.ts +30 -0
  174. package/dist/orchestration/delivery.d.ts.map +1 -0
  175. package/dist/orchestration/delivery.js +219 -0
  176. package/dist/orchestration/delivery.js.map +1 -0
  177. package/dist/orchestration/display-stream.d.ts +4 -0
  178. package/dist/orchestration/display-stream.d.ts.map +1 -0
  179. package/dist/orchestration/display-stream.js +77 -0
  180. package/dist/orchestration/display-stream.js.map +1 -0
  181. package/dist/orchestration/events.d.ts +30 -0
  182. package/dist/orchestration/events.d.ts.map +1 -0
  183. package/dist/orchestration/events.js +65 -0
  184. package/dist/orchestration/events.js.map +1 -0
  185. package/dist/orchestration/execution-observation.d.ts +38 -0
  186. package/dist/orchestration/execution-observation.d.ts.map +1 -0
  187. package/dist/orchestration/execution-observation.js +25 -0
  188. package/dist/orchestration/execution-observation.js.map +1 -0
  189. package/dist/orchestration/file-delivery.d.ts +16 -0
  190. package/dist/orchestration/file-delivery.d.ts.map +1 -0
  191. package/dist/orchestration/file-delivery.js +139 -0
  192. package/dist/orchestration/file-delivery.js.map +1 -0
  193. package/dist/orchestration/gateway-config.d.ts +17 -0
  194. package/dist/orchestration/gateway-config.d.ts.map +1 -0
  195. package/dist/orchestration/gateway-config.js +128 -0
  196. package/dist/orchestration/gateway-config.js.map +1 -0
  197. package/dist/orchestration/history.d.ts +13 -0
  198. package/dist/orchestration/history.d.ts.map +1 -0
  199. package/dist/orchestration/history.js +48 -0
  200. package/dist/orchestration/history.js.map +1 -0
  201. package/dist/orchestration/identity.d.ts +6 -0
  202. package/dist/orchestration/identity.d.ts.map +1 -0
  203. package/dist/orchestration/identity.js +13 -0
  204. package/dist/orchestration/identity.js.map +1 -0
  205. package/dist/orchestration/inference-errors.d.ts +3 -0
  206. package/dist/orchestration/inference-errors.d.ts.map +1 -0
  207. package/dist/orchestration/inference-errors.js +16 -0
  208. package/dist/orchestration/inference-errors.js.map +1 -0
  209. package/dist/orchestration/input-images.d.ts +12 -0
  210. package/dist/orchestration/input-images.d.ts.map +1 -0
  211. package/dist/orchestration/input-images.js +66 -0
  212. package/dist/orchestration/input-images.js.map +1 -0
  213. package/dist/orchestration/instance-lock.d.ts +5 -0
  214. package/dist/orchestration/instance-lock.d.ts.map +1 -0
  215. package/dist/orchestration/instance-lock.js +33 -0
  216. package/dist/orchestration/instance-lock.js.map +1 -0
  217. package/dist/orchestration/line-loading.d.ts +21 -0
  218. package/dist/orchestration/line-loading.d.ts.map +1 -0
  219. package/dist/orchestration/line-loading.js +89 -0
  220. package/dist/orchestration/line-loading.js.map +1 -0
  221. package/dist/orchestration/media.d.ts +3 -0
  222. package/dist/orchestration/media.d.ts.map +1 -0
  223. package/dist/orchestration/media.js +46 -0
  224. package/dist/orchestration/media.js.map +1 -0
  225. package/dist/orchestration/migrations/schema.d.ts +3 -0
  226. package/dist/orchestration/migrations/schema.d.ts.map +1 -0
  227. package/dist/orchestration/migrations/schema.js +80 -0
  228. package/dist/orchestration/migrations/schema.js.map +1 -0
  229. package/dist/orchestration/notification-mailbox.d.ts +5 -0
  230. package/dist/orchestration/notification-mailbox.d.ts.map +1 -0
  231. package/dist/orchestration/notification-mailbox.js +29 -0
  232. package/dist/orchestration/notification-mailbox.js.map +1 -0
  233. package/dist/orchestration/process-activity.d.ts +23 -0
  234. package/dist/orchestration/process-activity.d.ts.map +1 -0
  235. package/dist/orchestration/process-activity.js +95 -0
  236. package/dist/orchestration/process-activity.js.map +1 -0
  237. package/dist/orchestration/process-supervisor.d.ts +15 -0
  238. package/dist/orchestration/process-supervisor.d.ts.map +1 -0
  239. package/dist/orchestration/process-supervisor.js +92 -0
  240. package/dist/orchestration/process-supervisor.js.map +1 -0
  241. package/dist/orchestration/process-turn.d.ts +35 -0
  242. package/dist/orchestration/process-turn.d.ts.map +1 -0
  243. package/dist/orchestration/process-turn.js +246 -0
  244. package/dist/orchestration/process-turn.js.map +1 -0
  245. package/dist/orchestration/progress-review.d.ts +28 -0
  246. package/dist/orchestration/progress-review.d.ts.map +1 -0
  247. package/dist/orchestration/progress-review.js +48 -0
  248. package/dist/orchestration/progress-review.js.map +1 -0
  249. package/dist/orchestration/recovery.d.ts +8 -0
  250. package/dist/orchestration/recovery.d.ts.map +1 -0
  251. package/dist/orchestration/recovery.js +35 -0
  252. package/dist/orchestration/recovery.js.map +1 -0
  253. package/dist/orchestration/reply-context.d.ts +7 -0
  254. package/dist/orchestration/reply-context.d.ts.map +1 -0
  255. package/dist/orchestration/reply-context.js +54 -0
  256. package/dist/orchestration/reply-context.js.map +1 -0
  257. package/dist/orchestration/runtime.d.ts +232 -0
  258. package/dist/orchestration/runtime.d.ts.map +1 -0
  259. package/dist/orchestration/runtime.js +962 -0
  260. package/dist/orchestration/runtime.js.map +1 -0
  261. package/dist/orchestration/skills.d.ts +17 -0
  262. package/dist/orchestration/skills.d.ts.map +1 -0
  263. package/dist/orchestration/skills.js +32 -0
  264. package/dist/orchestration/skills.js.map +1 -0
  265. package/dist/orchestration/source-policy.d.ts +6 -0
  266. package/dist/orchestration/source-policy.d.ts.map +1 -0
  267. package/dist/orchestration/source-policy.js +16 -0
  268. package/dist/orchestration/source-policy.js.map +1 -0
  269. package/dist/orchestration/speech.d.ts +24 -0
  270. package/dist/orchestration/speech.d.ts.map +1 -0
  271. package/dist/orchestration/speech.js +61 -0
  272. package/dist/orchestration/speech.js.map +1 -0
  273. package/dist/orchestration/stop-controls.d.ts +30 -0
  274. package/dist/orchestration/stop-controls.d.ts.map +1 -0
  275. package/dist/orchestration/stop-controls.js +79 -0
  276. package/dist/orchestration/stop-controls.js.map +1 -0
  277. package/dist/orchestration/store.d.ts +86 -0
  278. package/dist/orchestration/store.d.ts.map +1 -0
  279. package/dist/orchestration/store.js +275 -0
  280. package/dist/orchestration/store.js.map +1 -0
  281. package/dist/orchestration/task-controls.d.ts +130 -0
  282. package/dist/orchestration/task-controls.d.ts.map +1 -0
  283. package/dist/orchestration/task-controls.js +55 -0
  284. package/dist/orchestration/task-controls.js.map +1 -0
  285. package/dist/orchestration/task-files.d.ts +29 -0
  286. package/dist/orchestration/task-files.d.ts.map +1 -0
  287. package/dist/orchestration/task-files.js +200 -0
  288. package/dist/orchestration/task-files.js.map +1 -0
  289. package/dist/orchestration/task-report.d.ts +7 -0
  290. package/dist/orchestration/task-report.d.ts.map +1 -0
  291. package/dist/orchestration/task-report.js +33 -0
  292. package/dist/orchestration/task-report.js.map +1 -0
  293. package/dist/orchestration/tasks/checkpoint-hook.d.ts +19 -0
  294. package/dist/orchestration/tasks/checkpoint-hook.d.ts.map +1 -0
  295. package/dist/orchestration/tasks/checkpoint-hook.js +49 -0
  296. package/dist/orchestration/tasks/checkpoint-hook.js.map +1 -0
  297. package/dist/orchestration/tasks/cleanup.d.ts +18 -0
  298. package/dist/orchestration/tasks/cleanup.d.ts.map +1 -0
  299. package/dist/orchestration/tasks/cleanup.js +113 -0
  300. package/dist/orchestration/tasks/cleanup.js.map +1 -0
  301. package/dist/orchestration/tasks/driver.d.ts +23 -0
  302. package/dist/orchestration/tasks/driver.d.ts.map +1 -0
  303. package/dist/orchestration/tasks/driver.js +209 -0
  304. package/dist/orchestration/tasks/driver.js.map +1 -0
  305. package/dist/orchestration/tasks/failure.d.ts +5 -0
  306. package/dist/orchestration/tasks/failure.d.ts.map +1 -0
  307. package/dist/orchestration/tasks/failure.js +17 -0
  308. package/dist/orchestration/tasks/failure.js.map +1 -0
  309. package/dist/orchestration/tasks/pool.d.ts +18 -0
  310. package/dist/orchestration/tasks/pool.d.ts.map +1 -0
  311. package/dist/orchestration/tasks/pool.js +73 -0
  312. package/dist/orchestration/tasks/pool.js.map +1 -0
  313. package/dist/orchestration/tasks/scheduler.d.ts +38 -0
  314. package/dist/orchestration/tasks/scheduler.d.ts.map +1 -0
  315. package/dist/orchestration/tasks/scheduler.js +160 -0
  316. package/dist/orchestration/tasks/scheduler.js.map +1 -0
  317. package/dist/orchestration/tasks/service.d.ts +109 -0
  318. package/dist/orchestration/tasks/service.d.ts.map +1 -0
  319. package/dist/orchestration/tasks/service.js +697 -0
  320. package/dist/orchestration/tasks/service.js.map +1 -0
  321. package/dist/orchestration/tasks/shared-workspace.d.ts +25 -0
  322. package/dist/orchestration/tasks/shared-workspace.d.ts.map +1 -0
  323. package/dist/orchestration/tasks/shared-workspace.js +162 -0
  324. package/dist/orchestration/tasks/shared-workspace.js.map +1 -0
  325. package/dist/orchestration/tasks/task-directive.d.ts +6 -0
  326. package/dist/orchestration/tasks/task-directive.d.ts.map +1 -0
  327. package/dist/orchestration/tasks/task-directive.js +41 -0
  328. package/dist/orchestration/tasks/task-directive.js.map +1 -0
  329. package/dist/orchestration/tasks/timing.d.ts +15 -0
  330. package/dist/orchestration/tasks/timing.d.ts.map +1 -0
  331. package/dist/orchestration/tasks/timing.js +48 -0
  332. package/dist/orchestration/tasks/timing.js.map +1 -0
  333. package/dist/orchestration/tasks/tool-repetition.d.ts +3 -0
  334. package/dist/orchestration/tasks/tool-repetition.d.ts.map +1 -0
  335. package/dist/orchestration/tasks/tool-repetition.js +30 -0
  336. package/dist/orchestration/tasks/tool-repetition.js.map +1 -0
  337. package/dist/orchestration/tasks/workspace.d.ts +24 -0
  338. package/dist/orchestration/tasks/workspace.d.ts.map +1 -0
  339. package/dist/orchestration/tasks/workspace.js +147 -0
  340. package/dist/orchestration/tasks/workspace.js.map +1 -0
  341. package/dist/orchestration/telegram-speech.d.ts +17 -0
  342. package/dist/orchestration/telegram-speech.d.ts.map +1 -0
  343. package/dist/orchestration/telegram-speech.js +55 -0
  344. package/dist/orchestration/telegram-speech.js.map +1 -0
  345. package/dist/orchestration/telegram-tool-status.d.ts +23 -0
  346. package/dist/orchestration/telegram-tool-status.d.ts.map +1 -0
  347. package/dist/orchestration/telegram-tool-status.js +207 -0
  348. package/dist/orchestration/telegram-tool-status.js.map +1 -0
  349. package/dist/orchestration/telegram-voices.d.ts +42 -0
  350. package/dist/orchestration/telegram-voices.d.ts.map +1 -0
  351. package/dist/orchestration/telegram-voices.js +70 -0
  352. package/dist/orchestration/telegram-voices.js.map +1 -0
  353. package/dist/orchestration/tool-activity.d.ts +10 -0
  354. package/dist/orchestration/tool-activity.d.ts.map +1 -0
  355. package/dist/orchestration/tool-activity.js +38 -0
  356. package/dist/orchestration/tool-activity.js.map +1 -0
  357. package/dist/orchestration/types.d.ts +177 -0
  358. package/dist/orchestration/types.d.ts.map +1 -0
  359. package/dist/orchestration/types.js +13 -0
  360. package/dist/orchestration/types.js.map +1 -0
  361. package/dist/orchestration/voice-reply-policy.d.ts +8 -0
  362. package/dist/orchestration/voice-reply-policy.d.ts.map +1 -0
  363. package/dist/orchestration/voice-reply-policy.js +46 -0
  364. package/dist/orchestration/voice-reply-policy.js.map +1 -0
  365. package/dist/orchestration/worker-shares.d.ts +8 -0
  366. package/dist/orchestration/worker-shares.d.ts.map +1 -0
  367. package/dist/orchestration/worker-shares.js +59 -0
  368. package/dist/orchestration/worker-shares.js.map +1 -0
  369. package/dist/orchestration/workflow.d.ts +121 -0
  370. package/dist/orchestration/workflow.d.ts.map +1 -0
  371. package/dist/orchestration/workflow.js +69 -0
  372. package/dist/orchestration/workflow.js.map +1 -0
  373. package/dist/session/input-image.d.ts +10 -0
  374. package/dist/session/input-image.d.ts.map +1 -0
  375. package/dist/session/input-image.js +3 -0
  376. package/dist/session/input-image.js.map +1 -0
  377. package/dist/session/process.d.ts +15 -3
  378. package/dist/session/process.d.ts.map +1 -1
  379. package/dist/session/process.js +144 -35
  380. package/dist/session/process.js.map +1 -1
  381. package/dist/session/runtime-profile.d.ts +36 -0
  382. package/dist/session/runtime-profile.d.ts.map +1 -0
  383. package/dist/session/runtime-profile.js +63 -0
  384. package/dist/session/runtime-profile.js.map +1 -0
  385. package/dist/session/store.d.ts.map +1 -1
  386. package/dist/session/store.js +23 -1
  387. package/dist/session/store.js.map +1 -1
  388. package/dist/share/share-store.d.ts +3 -1
  389. package/dist/share/share-store.d.ts.map +1 -1
  390. package/dist/share/share-store.js +13 -2
  391. package/dist/share/share-store.js.map +1 -1
  392. package/dist/shared/line-request-order.d.ts +2 -0
  393. package/dist/shared/line-request-order.d.ts.map +1 -0
  394. package/dist/shared/line-request-order.js +16 -0
  395. package/dist/shared/line-request-order.js.map +1 -0
  396. package/dist/shared/task-elapsed.d.ts +3 -0
  397. package/dist/shared/task-elapsed.d.ts.map +1 -0
  398. package/dist/shared/task-elapsed.js +12 -0
  399. package/dist/shared/task-elapsed.js.map +1 -0
  400. package/dist/shared/task-updated-time.d.ts +3 -0
  401. package/dist/shared/task-updated-time.d.ts.map +1 -0
  402. package/dist/shared/task-updated-time.js +15 -0
  403. package/dist/shared/task-updated-time.js.map +1 -0
  404. package/dist/skills/loader.d.ts +2 -0
  405. package/dist/skills/loader.d.ts.map +1 -1
  406. package/dist/skills/loader.js.map +1 -1
  407. package/dist/telegram/chunks.d.ts +19 -0
  408. package/dist/telegram/chunks.d.ts.map +1 -0
  409. package/dist/telegram/chunks.js +129 -0
  410. package/dist/telegram/chunks.js.map +1 -0
  411. package/dist/telegram/markdown.d.ts.map +1 -1
  412. package/dist/telegram/markdown.js +18 -3
  413. package/dist/telegram/markdown.js.map +1 -1
  414. package/dist/telegram/receiver.d.ts +2 -1
  415. package/dist/telegram/receiver.d.ts.map +1 -1
  416. package/dist/telegram/receiver.js +6 -1
  417. package/dist/telegram/receiver.js.map +1 -1
  418. package/dist/types.d.ts +54 -0
  419. package/dist/types.d.ts.map +1 -1
  420. package/dist/ui/web-ui.d.ts.map +1 -1
  421. package/dist/ui/web-ui.js +31 -5
  422. package/dist/ui/web-ui.js.map +1 -1
  423. package/dist/voice/diagnostics.d.ts +29 -0
  424. package/dist/voice/diagnostics.d.ts.map +1 -0
  425. package/dist/voice/diagnostics.js +37 -0
  426. package/dist/voice/diagnostics.js.map +1 -0
  427. package/dist/voice/errors.d.ts +19 -0
  428. package/dist/voice/errors.d.ts.map +1 -0
  429. package/dist/voice/errors.js +133 -0
  430. package/dist/voice/errors.js.map +1 -0
  431. package/dist/voice/line-audio.d.ts +4 -0
  432. package/dist/voice/line-audio.d.ts.map +1 -0
  433. package/dist/voice/line-audio.js +27 -0
  434. package/dist/voice/line-audio.js.map +1 -0
  435. package/dist/voice/managed-quota.d.ts +5 -0
  436. package/dist/voice/managed-quota.d.ts.map +1 -0
  437. package/dist/voice/managed-quota.js +35 -0
  438. package/dist/voice/managed-quota.js.map +1 -0
  439. package/dist/voice/mp3.d.ts +3 -0
  440. package/dist/voice/mp3.d.ts.map +1 -0
  441. package/dist/voice/mp3.js +30 -0
  442. package/dist/voice/mp3.js.map +1 -0
  443. package/dist/voice/notes.d.ts +9 -0
  444. package/dist/voice/notes.d.ts.map +1 -0
  445. package/dist/voice/notes.js +68 -0
  446. package/dist/voice/notes.js.map +1 -0
  447. package/dist/voice/playback.d.ts +22 -0
  448. package/dist/voice/playback.d.ts.map +1 -0
  449. package/dist/voice/playback.js +52 -0
  450. package/dist/voice/playback.js.map +1 -0
  451. package/dist/voice/preview.d.ts +6 -0
  452. package/dist/voice/preview.d.ts.map +1 -0
  453. package/dist/voice/preview.js +44 -0
  454. package/dist/voice/preview.js.map +1 -0
  455. package/dist/voice/protocol.d.ts +10 -0
  456. package/dist/voice/protocol.d.ts.map +1 -0
  457. package/dist/voice/protocol.js +30 -0
  458. package/dist/voice/protocol.js.map +1 -0
  459. package/dist/voice/providers/cartesia-tts.d.ts +26 -0
  460. package/dist/voice/providers/cartesia-tts.d.ts.map +1 -0
  461. package/dist/voice/providers/cartesia-tts.js +95 -0
  462. package/dist/voice/providers/cartesia-tts.js.map +1 -0
  463. package/dist/voice/providers/deepgram-stt.d.ts +20 -0
  464. package/dist/voice/providers/deepgram-stt.d.ts.map +1 -0
  465. package/dist/voice/providers/deepgram-stt.js +66 -0
  466. package/dist/voice/providers/deepgram-stt.js.map +1 -0
  467. package/dist/voice/providers/elevenlabs-stt.d.ts +20 -0
  468. package/dist/voice/providers/elevenlabs-stt.d.ts.map +1 -0
  469. package/dist/voice/providers/elevenlabs-stt.js +92 -0
  470. package/dist/voice/providers/elevenlabs-stt.js.map +1 -0
  471. package/dist/voice/providers/elevenlabs-tts.d.ts +26 -0
  472. package/dist/voice/providers/elevenlabs-tts.d.ts.map +1 -0
  473. package/dist/voice/providers/elevenlabs-tts.js +82 -0
  474. package/dist/voice/providers/elevenlabs-tts.js.map +1 -0
  475. package/dist/voice/providers/encoded-audio-tts.d.ts +21 -0
  476. package/dist/voice/providers/encoded-audio-tts.d.ts.map +1 -0
  477. package/dist/voice/providers/encoded-audio-tts.js +175 -0
  478. package/dist/voice/providers/encoded-audio-tts.js.map +1 -0
  479. package/dist/voice/providers/fake.d.ts +39 -0
  480. package/dist/voice/providers/fake.d.ts.map +1 -0
  481. package/dist/voice/providers/fake.js +41 -0
  482. package/dist/voice/providers/fake.js.map +1 -0
  483. package/dist/voice/providers/gemini-live-stt.d.ts +21 -0
  484. package/dist/voice/providers/gemini-live-stt.d.ts.map +1 -0
  485. package/dist/voice/providers/gemini-live-stt.js +158 -0
  486. package/dist/voice/providers/gemini-live-stt.js.map +1 -0
  487. package/dist/voice/providers/gemini.d.ts +60 -0
  488. package/dist/voice/providers/gemini.d.ts.map +1 -0
  489. package/dist/voice/providers/gemini.js +124 -0
  490. package/dist/voice/providers/gemini.js.map +1 -0
  491. package/dist/voice/providers/model-ref.d.ts +9 -0
  492. package/dist/voice/providers/model-ref.d.ts.map +1 -0
  493. package/dist/voice/providers/model-ref.js +31 -0
  494. package/dist/voice/providers/model-ref.js.map +1 -0
  495. package/dist/voice/providers/openrouter.d.ts +39 -0
  496. package/dist/voice/providers/openrouter.d.ts.map +1 -0
  497. package/dist/voice/providers/openrouter.js +154 -0
  498. package/dist/voice/providers/openrouter.js.map +1 -0
  499. package/dist/voice/providers/paxalabs-stt.d.ts +12 -0
  500. package/dist/voice/providers/paxalabs-stt.d.ts.map +1 -0
  501. package/dist/voice/providers/paxalabs-stt.js +41 -0
  502. package/dist/voice/providers/paxalabs-stt.js.map +1 -0
  503. package/dist/voice/providers/paxalabs-tts.d.ts +23 -0
  504. package/dist/voice/providers/paxalabs-tts.d.ts.map +1 -0
  505. package/dist/voice/providers/paxalabs-tts.js +80 -0
  506. package/dist/voice/providers/paxalabs-tts.js.map +1 -0
  507. package/dist/voice/providers/recorded-stt.d.ts +34 -0
  508. package/dist/voice/providers/recorded-stt.d.ts.map +1 -0
  509. package/dist/voice/providers/recorded-stt.js +59 -0
  510. package/dist/voice/providers/recorded-stt.js.map +1 -0
  511. package/dist/voice/providers/registry.d.ts +10 -0
  512. package/dist/voice/providers/registry.d.ts.map +1 -0
  513. package/dist/voice/providers/registry.js +77 -0
  514. package/dist/voice/providers/registry.js.map +1 -0
  515. package/dist/voice/providers/socket.d.ts +10 -0
  516. package/dist/voice/providers/socket.d.ts.map +1 -0
  517. package/dist/voice/providers/socket.js +91 -0
  518. package/dist/voice/providers/socket.js.map +1 -0
  519. package/dist/voice/providers/upstream.d.ts +9 -0
  520. package/dist/voice/providers/upstream.d.ts.map +1 -0
  521. package/dist/voice/providers/upstream.js +51 -0
  522. package/dist/voice/providers/upstream.js.map +1 -0
  523. package/dist/voice/providers/voice-catalog.d.ts +17 -0
  524. package/dist/voice/providers/voice-catalog.d.ts.map +1 -0
  525. package/dist/voice/providers/voice-catalog.js +167 -0
  526. package/dist/voice/providers/voice-catalog.js.map +1 -0
  527. package/dist/voice/queue.d.ts +2 -0
  528. package/dist/voice/queue.d.ts.map +1 -0
  529. package/dist/voice/queue.js +6 -0
  530. package/dist/voice/queue.js.map +1 -0
  531. package/dist/voice/session.d.ts +86 -0
  532. package/dist/voice/session.d.ts.map +1 -0
  533. package/dist/voice/session.js +464 -0
  534. package/dist/voice/session.js.map +1 -0
  535. package/dist/voice/turn-manager.d.ts +27 -0
  536. package/dist/voice/turn-manager.d.ts.map +1 -0
  537. package/dist/voice/turn-manager.js +75 -0
  538. package/dist/voice/turn-manager.js.map +1 -0
  539. package/dist/voice/types.d.ts +78 -0
  540. package/dist/voice/types.d.ts.map +1 -0
  541. package/dist/voice/types.js +18 -0
  542. package/dist/voice/types.js.map +1 -0
  543. package/dist/voice/wav.d.ts +2 -0
  544. package/dist/voice/wav.d.ts.map +1 -0
  545. package/dist/voice/wav.js +21 -0
  546. package/dist/voice/wav.js.map +1 -0
  547. package/dist/wechat/ilink-client.d.ts +207 -0
  548. package/dist/wechat/ilink-client.d.ts.map +1 -0
  549. package/dist/wechat/ilink-client.js +660 -0
  550. package/dist/wechat/ilink-client.js.map +1 -0
  551. package/dist/wechat/manager.d.ts +93 -0
  552. package/dist/wechat/manager.d.ts.map +1 -0
  553. package/dist/wechat/manager.js +409 -0
  554. package/dist/wechat/manager.js.map +1 -0
  555. package/dist/whatsapp/manager.d.ts.map +1 -1
  556. package/dist/whatsapp/manager.js +51 -1
  557. package/dist/whatsapp/manager.js.map +1 -1
  558. package/mcp/capability-catalog.ts +206 -0
  559. package/mcp/modules.ts +56 -0
  560. package/mcp/server.ts +18 -35
  561. package/mcp/tools/browser/module.ts +1 -1
  562. package/mcp/tools/browser/skills/open-browser/SKILL.md +5 -3
  563. package/mcp/tools/discord/commands.ts +17 -1
  564. package/mcp/tools/discord/inbound.ts +11 -0
  565. package/mcp/tools/discord/module.ts +42 -2
  566. package/mcp/tools/discord/receiver-server.ts +10 -0
  567. package/mcp/tools/discord/types.ts +4 -1
  568. package/mcp/tools/image/module.ts +3 -1
  569. package/mcp/tools/receiver-spool.test.ts +28 -0
  570. package/mcp/tools/receiver-spool.ts +56 -0
  571. package/mcp/tools/share-file/module.ts +9 -1
  572. package/mcp/tools/shared/share-client.ts +19 -0
  573. package/mcp/tools/tasks/module.ts +35 -0
  574. package/mcp/tools/telegram/media-group.ts +21 -0
  575. package/mcp/tools/telegram/receiver-server.ts +232 -11
  576. package/mcp/tools/telegram/selection-confirmation.ts +20 -0
  577. package/mcp/tools/telegram/task-browser.ts +117 -0
  578. package/mcp/tools/telegram/task-detail.ts +36 -0
  579. package/mcp/tools/telegram/task-elapsed.ts +1 -0
  580. package/mcp/tools/telegram/typing.ts +6 -116
  581. package/mcp/tools/video/module.ts +1 -1
  582. package/mcp/tools/wechat/client.ts +49 -0
  583. package/mcp/tools/wechat/module.ts +88 -0
  584. package/mcp/types.ts +5 -0
  585. package/package.json +2 -1
  586. package/resource/claude_gateway.jpeg +0 -0
  587. package/scripts/orchestration/bench-store.cjs +30 -0
  588. package/scripts/orchestration/inspect.cjs +16 -0
  589. package/scripts/orchestration/probe-profile.cjs +67 -0
  590. package/scripts/orchestration/reconcile.cjs +21 -0
  591. package/scripts/orchestration/smoke-elevenlabs.cjs +90 -0
  592. package/scripts/orchestration/smoke-media.cjs +70 -0
  593. package/scripts/orchestration/smoke-text.cjs +101 -0
  594. package/scripts/orchestration/smoke-vision.cjs +49 -0
@@ -0,0 +1,660 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.encodeClientVersion = encodeClientVersion;
37
+ exports.normalizeQrImage = normalizeQrImage;
38
+ exports.resolveWeixinReply = resolveWeixinReply;
39
+ exports.resolveWeixinImageRef = resolveWeixinImageRef;
40
+ exports.resolveWeixinFileRef = resolveWeixinFileRef;
41
+ exports.aes128EcbDecryptPermissive = aes128EcbDecryptPermissive;
42
+ exports.downloadWeixinImage = downloadWeixinImage;
43
+ exports.createILinkClient = createILinkClient;
44
+ exports.createFakeILinkClient = createFakeILinkClient;
45
+ exports.isWeChatILinkFakeEnabled = isWeChatILinkFakeEnabled;
46
+ /**
47
+ * HTTP client for Tencent's iLink Bot API ("WeChat ClawBot") — the real,
48
+ * documented, self-serve protocol behind both source projects this channel
49
+ * was researched from (Hermes-agent's Weixin adapter, OpenClaw's WeChat
50
+ * plugin). The canonical protocol reference is Tencent's own
51
+ * `Tencent/openclaw-weixin` repo (docs/protocol.md), an MIT-licensed,
52
+ * actively-maintained OpenClaw channel plugin published as
53
+ * `@tencent-weixin/openclaw-weixin` — not a third-party paid bridge. No
54
+ * account signup with any company is required: `openclaw channels login`
55
+ * scans a QR with the user's own WeChat app and stores the resulting
56
+ * credentials locally, which is exactly the shape `startLinking()` below
57
+ * mirrors.
58
+ *
59
+ * IMPORTANT caveats carried over from that protocol doc, not filled in here:
60
+ * - `qrcode_img_content` — confirmed against a real response (2026-09-10):
61
+ * it is a `liteapp.weixin.qq.com` URL (an `text/html` mini-program deep
62
+ * link, NOT an image — confirmed by fetching it directly and checking
63
+ * Content-Type), meant to be scanned as data, not displayed as a
64
+ * pre-rendered picture. `normalizeQrImage()` below QR-encodes it into an
65
+ * actual PNG data URI (via the `qrcode` package) so `WeChatStatus.qr`
66
+ * keeps the same "ready-to-`<img src>`" contract every other device-linked
67
+ * channel's status already uses (mirrors WhatsApp/Baileys, which does the
68
+ * same QR-image encoding for its own pairing string).
69
+ * - `need_verifycode`/`verify_code_blocked` (an extra verification-code
70
+ * step some accounts hit) has no UI path yet — those statuses currently
71
+ * fall back to "expired" (ask the user to retry) rather than prompting
72
+ * for a code. See `pollLinkStatus`'s doc comment.
73
+ * - Media: inbound IMAGES (item type 2) are downloaded + decrypted (see
74
+ * `resolveWeixinImageRef`/`downloadWeixinImage` below, confirmed live
75
+ * 2026-09-11 against protocol.md + `NousResearch/hermes-agent`'s working
76
+ * Python implementation). Voice/file/video (types 3/4/5) and ALL outbound
77
+ * media sending are still out of scope — only `text_item` is written.
78
+ */
79
+ const crypto = __importStar(require("crypto"));
80
+ const QRCode = __importStar(require("qrcode"));
81
+ const DEFAULT_ILINK_BASE_URL = 'https://ilinkai.weixin.qq.com';
82
+ /** `base_info.bot_agent` — a short ASCII observability tag, per protocol.md's
83
+ * "sanitized observability identifier... not used for authentication or
84
+ * routing" — analogous to `bot_agent: "OpenClaw"` in Tencent's own examples.
85
+ * This gateway has no single fixed downstream product identity, so this is
86
+ * only the fallback when a deployer doesn't set `AgentConfig.wechat.botAgent`
87
+ * — see that field's doc comment. Mirrors `channels.openclaw-weixin.botAgent`
88
+ * in `Tencent/openclaw-weixin`'s own config being per-deployment, not fixed. */
89
+ const DEFAULT_BOT_AGENT = 'claude-gateway';
90
+ /** `base_info.channel_version` — this integration's own version, not the
91
+ * gateway's. Bump when this file's request/response handling changes. */
92
+ const CHANNEL_VERSION = '1.0.0';
93
+ /** Random uint32 → decimal string → base64, per protocol.md's `X-WECHAT-UIN` spec. */
94
+ function randomWechatUin() {
95
+ const n = Math.floor(Math.random() * 0x100000000);
96
+ return Buffer.from(String(n), 'utf-8').toString('base64');
97
+ }
98
+ /**
99
+ * Plugin version as `0x00MMNNPP` (major/minor/patch, one byte each) rendered
100
+ * as a decimal string, per protocol.md's `iLink-App-ClientVersion` spec.
101
+ */
102
+ function encodeClientVersion(version) {
103
+ const [major = 0, minor = 0, patch = 0] = version.split('.').map((n) => parseInt(n, 10) || 0);
104
+ const encoded = ((major & 0xff) << 16) | ((minor & 0xff) << 8) | (patch & 0xff);
105
+ return String(encoded);
106
+ }
107
+ const CLIENT_VERSION_HEADER = encodeClientVersion(CHANNEL_VERSION);
108
+ /**
109
+ * Turn whatever `qrcode_img_content` actually is into a ready-to-`<img src>`
110
+ * PNG data URI. Confirmed shape (see module doc comment): a
111
+ * `liteapp.weixin.qq.com` URL that must itself be QR-encoded — it is data to
112
+ * scan, not a picture to show as-is.
113
+ */
114
+ async function normalizeQrImage(content) {
115
+ if (content.startsWith('data:'))
116
+ return content;
117
+ if (content.startsWith('http://') || content.startsWith('https://')) {
118
+ return QRCode.toDataURL(content, { margin: 1 });
119
+ }
120
+ // Fallback for a shape neither confirmed response nor the doc describes:
121
+ // assume base64-encoded PNG bytes already.
122
+ return `data:image/png;base64,${content}`;
123
+ }
124
+ const DEFAULT_FETCH_TIMEOUT_MS = 15000;
125
+ /**
126
+ * `redirect_host` (and `baseurl` on a `confirmed` response) — confirmed live
127
+ * (2026-09-10) to arrive as a bare host, e.g. `ilinkai2.weixin.qq.com`, NOT a
128
+ * full `https://...` URL as protocol.md's naming implies. `new URL(path,
129
+ * base)` throws "Invalid URL" on a schemeless base, which silently wedged
130
+ * every poll after a real scan forever (the retry loop in
131
+ * WeChatManager.startLinking keeps calling this same host, gets the same
132
+ * throw every time, and only the attempt's 2-minute deadline ever ends it).
133
+ */
134
+ function normalizeIlinkHost(host) {
135
+ return /^https?:\/\//i.test(host) ? host : `https://${host}`;
136
+ }
137
+ async function ilinkFetch(opts) {
138
+ let url;
139
+ try {
140
+ url = new URL(opts.path, opts.baseUrl);
141
+ }
142
+ catch {
143
+ // Bare `new URL()` errors ("Invalid URL") carry no context — surface
144
+ // which baseUrl actually failed, since this is exactly what a malformed
145
+ // `redirect_host`/`baseurl` from iLink looks like (see normalizeIlinkHost).
146
+ throw new Error(`iLink ${opts.method} ${opts.path}: invalid baseUrl "${opts.baseUrl}"`);
147
+ }
148
+ if (opts.query) {
149
+ for (const [k, v] of Object.entries(opts.query))
150
+ url.searchParams.set(k, v);
151
+ }
152
+ const headers = {
153
+ 'iLink-App-Id': 'bot',
154
+ 'iLink-App-ClientVersion': CLIENT_VERSION_HEADER,
155
+ // Force a fresh connection per request instead of reusing Node's
156
+ // undici keep-alive pool. Confirmed root cause, sourced from a second
157
+ // independent reference implementation (Hermes-agent's
158
+ // `gateway/platforms/weixin.py`, `_make_ssl_connector()`): "proxies like
159
+ // Cloudflare Warp leave peer-initiated FIN in CLOSE_WAIT" — a pooled
160
+ // keep-alive connection can go stale (closed server-side) without the
161
+ // client noticing, and a later request reusing it just hangs/fails.
162
+ // Hermes-agent works around this with a 2s keepalive_timeout; the
163
+ // simpler equivalent here is to never keep the connection alive at all.
164
+ Connection: 'close',
165
+ };
166
+ // Auth headers are sent for the QR POST too (it still identifies the
167
+ // client), but NOT for the unauthenticated GET status-poll — see
168
+ // protocol.md's "Before auth (QR polling)" header table.
169
+ if (opts.method === 'POST') {
170
+ headers['Content-Type'] = 'application/json';
171
+ headers['AuthorizationType'] = 'ilink_bot_token';
172
+ headers['X-WECHAT-UIN'] = randomWechatUin();
173
+ }
174
+ if (opts.botToken)
175
+ headers['Authorization'] = `Bearer ${opts.botToken}`;
176
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
177
+ const controller = new AbortController();
178
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
179
+ let res;
180
+ try {
181
+ res = await fetch(url.toString(), {
182
+ method: opts.method,
183
+ headers,
184
+ body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,
185
+ signal: controller.signal,
186
+ });
187
+ }
188
+ catch (err) {
189
+ if (err.name === 'AbortError') {
190
+ throw new Error(`iLink ${opts.method} ${opts.path}: timed out after ${timeoutMs}ms`);
191
+ }
192
+ throw err;
193
+ }
194
+ finally {
195
+ clearTimeout(timer);
196
+ }
197
+ if (!res.ok) {
198
+ throw new Error(`iLink ${opts.method} ${opts.path}: HTTP ${res.status}`);
199
+ }
200
+ return (await res.json());
201
+ }
202
+ /** Extract the first text item's body — image items are handled separately by resolveWeixinImageRef; voice/file/video are still ignored. */
203
+ function textFromItems(items) {
204
+ return items?.find((i) => i.type === 1)?.text_item?.text;
205
+ }
206
+ /** Preserve the immediate quote only; nested quotes are not fresh user instructions. */
207
+ function resolveWeixinReply(items) {
208
+ const ref = items?.find(item => item.ref_msg)?.ref_msg;
209
+ if (!ref)
210
+ return;
211
+ const item = ref.message_item;
212
+ let image, file;
213
+ try {
214
+ image = resolveWeixinImageRef(item ? [item] : []);
215
+ }
216
+ catch { /* Existing media URL guard applies. */ }
217
+ try {
218
+ file = resolveWeixinFileRef(item ? [item] : []);
219
+ }
220
+ catch { /* Existing media URL guard applies. */ }
221
+ return { messageId: ref.svr_id === undefined ? undefined : String(ref.svr_id), text: item?.text_item?.text ?? ref.title, image, file };
222
+ }
223
+ /**
224
+ * Hosts Tencent actually serves inbound-media downloads from, per
225
+ * `NousResearch/hermes-agent`'s `gateway/platforms/weixin.py`
226
+ * (`_WEIXIN_CDN_ALLOWLIST`, confirmed live 2026-09-11). Only checked against
227
+ * a server-supplied `full_url` — a URL built from `encrypt_query_param`
228
+ * against `DEFAULT_CDN_BASE_URL` below doesn't need it (that host is our own
229
+ * trusted constant). This is an SSRF guard: without it, a malicious/buggy
230
+ * `full_url` could point this server's own outbound fetch at an arbitrary
231
+ * internal host.
232
+ */
233
+ const WEIXIN_CDN_ALLOWLIST = new Set([
234
+ 'novac2c.cdn.weixin.qq.com',
235
+ 'ilinkai.weixin.qq.com',
236
+ 'wx.qlogo.cn',
237
+ 'thirdwx.qlogo.cn',
238
+ 'res.wx.qq.com',
239
+ 'mmbiz.qpic.cn',
240
+ 'mmbiz.qlogo.cn',
241
+ // Confirmed live 2026-09-11: real `image_item.media.full_url` responses use
242
+ // the `.wechat.com` domain family, not `.weixin.qq.com` — same mismatch
243
+ // class as `redirect_host` earlier this session (docs/Hermes-agent's own
244
+ // allowlist assumed `.weixin.qq.com`; our own post-link `baseUrl` for
245
+ // authenticated calls is ALSO `ilinkai.wechat.com`, confirming this is the
246
+ // real production domain, not a one-off).
247
+ 'novac2c.cdn.wechat.com',
248
+ 'ilinkai.wechat.com',
249
+ ]);
250
+ const DEFAULT_CDN_BASE_URL = 'https://novac2c.cdn.weixin.qq.com/c2c';
251
+ /**
252
+ * Decode an inbound AES key from its `media.aes_key` (base64) encoding.
253
+ * protocol.md: "the download decoder accepts base64 of either 16 raw bytes
254
+ * or a 32-character hexadecimal key" — both shapes are used in practice.
255
+ */
256
+ function parseAesKeyBase64(aesKeyB64) {
257
+ const decoded = Buffer.from(aesKeyB64, 'base64');
258
+ if (decoded.length === 16)
259
+ return decoded;
260
+ if (decoded.length === 32) {
261
+ const text = decoded.toString('ascii');
262
+ if (/^[0-9a-fA-F]{32}$/.test(text))
263
+ return Buffer.from(text, 'hex');
264
+ }
265
+ throw new Error(`iLink image: unrecognized aes_key encoding (${decoded.length} bytes decoded)`);
266
+ }
267
+ /**
268
+ * Shared URL+key resolution for any media item's `media` block. `rawHexKey`
269
+ * is the item-type-specific top-level key field when one exists (only
270
+ * `image_item.aeskey` per protocol.md — files have no equivalent, so callers
271
+ * for other item types pass `undefined`).
272
+ */
273
+ function resolveMediaRef(media, rawHexKey) {
274
+ let aesKey;
275
+ if (rawHexKey && /^[0-9a-fA-F]{32}$/.test(rawHexKey)) {
276
+ aesKey = Buffer.from(rawHexKey, 'hex');
277
+ }
278
+ else if (media?.aes_key) {
279
+ aesKey = parseAesKeyBase64(media.aes_key);
280
+ }
281
+ let url;
282
+ if (media?.full_url) {
283
+ const host = new URL(media.full_url).hostname;
284
+ if (!WEIXIN_CDN_ALLOWLIST.has(host)) {
285
+ throw new Error(`iLink media: refusing to download from non-allowlisted host "${host}"`);
286
+ }
287
+ url = media.full_url;
288
+ }
289
+ else if (media?.encrypt_query_param) {
290
+ url = `${DEFAULT_CDN_BASE_URL}/download?encrypted_query_param=${encodeURIComponent(media.encrypt_query_param)}`;
291
+ }
292
+ return url ? { url, aesKey } : undefined;
293
+ }
294
+ function resolveWeixinImageRef(items) {
295
+ const item = items?.find((i) => i.type === 2 && i.image_item);
296
+ if (!item?.image_item)
297
+ return undefined;
298
+ return resolveMediaRef(item.image_item.media, item.image_item.aeskey);
299
+ }
300
+ /** Resolve an inbound file item (type=4) — protocol.md has no top-level aeskey for files, only media.aes_key. */
301
+ function resolveWeixinFileRef(items) {
302
+ const item = items?.find((i) => i.type === 4 && i.file_item);
303
+ if (!item?.file_item)
304
+ return undefined;
305
+ const ref = resolveMediaRef(item.file_item.media, undefined);
306
+ if (!ref)
307
+ return undefined;
308
+ const expectedLength = item.file_item.len ? parseInt(item.file_item.len, 10) : undefined;
309
+ return {
310
+ ...ref,
311
+ fileName: item.file_item.file_name || 'file',
312
+ expectedLength: Number.isFinite(expectedLength) ? expectedLength : undefined,
313
+ };
314
+ }
315
+ /**
316
+ * AES-128-ECB decrypt with PERMISSIVE PKCS#7 unpadding: if the trailing
317
+ * padding doesn't validate, return the padded bytes as-is instead of
318
+ * throwing. Mirrors `NousResearch/hermes-agent`'s `_aes128_ecb_decrypt`
319
+ * exactly (confirmed live 2026-09-11) — Node's built-in auto-unpad
320
+ * (`setAutoPadding(true)`) throws "bad decrypt" on the same real-world edge
321
+ * cases Hermes-agent's own implementation was written to tolerate.
322
+ */
323
+ function aes128EcbDecryptPermissive(ciphertext, key) {
324
+ const decipher = crypto.createDecipheriv('aes-128-ecb', key, null);
325
+ decipher.setAutoPadding(false);
326
+ const padded = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
327
+ if (padded.length === 0)
328
+ return padded;
329
+ const padLen = padded[padded.length - 1];
330
+ if (padLen >= 1 && padLen <= 16 && padded.length >= padLen) {
331
+ const tail = padded.subarray(padded.length - padLen);
332
+ if (tail.every((b) => b === padLen))
333
+ return padded.subarray(0, padded.length - padLen);
334
+ }
335
+ return padded;
336
+ }
337
+ /**
338
+ * Download (and decrypt, if `ref.aesKey` is set) an inbound image's bytes.
339
+ * No bot-token auth — protocol.md documents this as a plain CDN GET against
340
+ * a pre-signed/scoped URL.
341
+ */
342
+ /** 20 MB — matches MediaStore.maxUploadBytes (not imported directly: ilink-client is a
343
+ * protocol-layer module and shouldn't depend on history/media-store's storage layer). */
344
+ const DEFAULT_MAX_MEDIA_BYTES = 20 * 1024 * 1024;
345
+ async function downloadWeixinImage(ref, opts = {}) {
346
+ const timeoutMs = opts.timeoutMs ?? 30000;
347
+ const maxBytes = opts.maxBytes ?? DEFAULT_MAX_MEDIA_BYTES;
348
+ const controller = new AbortController();
349
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
350
+ let res;
351
+ try {
352
+ // redirect: 'manual' — WEIXIN_CDN_ALLOWLIST only validates ref.url's OWN
353
+ // host; fetch()'s default 'follow' would silently chase a redirect to
354
+ // any other host, defeating that check as an SSRF guard. A redirect
355
+ // response here is treated as a hard failure rather than re-validated,
356
+ // since the legitimate CDN paths (both the trusted default base and an
357
+ // allowlisted full_url) are not expected to redirect at all.
358
+ res = await fetch(ref.url, { signal: controller.signal, redirect: 'manual' });
359
+ }
360
+ finally {
361
+ clearTimeout(timer);
362
+ }
363
+ if (res.type === 'opaqueredirect' || (res.status >= 300 && res.status < 400)) {
364
+ throw new Error('iLink media download: refusing to follow a redirect (SSRF guard)');
365
+ }
366
+ if (!res.ok) {
367
+ throw new Error(`iLink image download: HTTP ${res.status}`);
368
+ }
369
+ // Reject on the declared size before buffering the body when the server
370
+ // tells us upfront (LINE's webhook router enforces the same cap the same
371
+ // way — see MAX_MEDIA_BYTES's declaredSize check there).
372
+ const declaredLength = Number(res.headers?.get?.('content-length'));
373
+ if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {
374
+ throw new Error(`iLink media download: declared size ${declaredLength} exceeds ${maxBytes} byte cap`);
375
+ }
376
+ const raw = Buffer.from(await res.arrayBuffer());
377
+ if (raw.length > maxBytes) {
378
+ throw new Error(`iLink media download: ${raw.length} bytes exceeds ${maxBytes} byte cap`);
379
+ }
380
+ const decrypted = ref.aesKey ? aes128EcbDecryptPermissive(raw, ref.aesKey) : raw;
381
+ // See ILinkImageRef.expectedLength's doc comment — trims a leading prefix
382
+ // WeChat's file-upload path adds ahead of the real content.
383
+ if (ref.expectedLength !== undefined && decrypted.length > ref.expectedLength) {
384
+ return decrypted.subarray(decrypted.length - ref.expectedLength);
385
+ }
386
+ return decrypted;
387
+ }
388
+ /**
389
+ * Real implementation — talks to Tencent's iLink Bot API. See this module's
390
+ * doc comment for the two documented ambiguities (`qrcode_img_content`'s
391
+ * encoding, and the unhandled verification-code statuses) still to confirm
392
+ * against a real account.
393
+ */
394
+ function createILinkClient(baseUrl, botAgent) {
395
+ const qrBaseUrl = baseUrl || DEFAULT_ILINK_BASE_URL;
396
+ const resolvedBotAgent = botAgent || DEFAULT_BOT_AGENT;
397
+ const clientBaseInfo = () => ({
398
+ channel_version: CHANNEL_VERSION,
399
+ bot_agent: resolvedBotAgent,
400
+ });
401
+ // getupdates' cursor must survive across polls — scoped per accountId so
402
+ // one client instance could in principle serve more than one credential
403
+ // set, even though v1 only ever uses it for a single linked account.
404
+ const updateCursors = new Map();
405
+ // Which host to poll get_qrcode_status on next, per in-flight login
406
+ // attempt — starts at qrBaseUrl, switches once `redirect_host` is seen
407
+ // (see GetQrcodeStatusResponse's doc comment on that field for why this
408
+ // exists at all).
409
+ const qrStatusHosts = new Map();
410
+ return {
411
+ async requestLinkQr() {
412
+ const res = await ilinkFetch({
413
+ method: 'POST',
414
+ path: '/ilink/bot/get_bot_qrcode',
415
+ query: { bot_type: '3' },
416
+ body: { local_token_list: [] },
417
+ baseUrl: qrBaseUrl,
418
+ });
419
+ return { qrDataUri: await normalizeQrImage(res.qrcode_img_content), loginSessionId: res.qrcode };
420
+ },
421
+ async pollLinkStatus(loginSessionId) {
422
+ const pollHost = qrStatusHosts.get(loginSessionId) ?? qrBaseUrl;
423
+ const res = await ilinkFetch({
424
+ method: 'GET',
425
+ path: '/ilink/bot/get_qrcode_status',
426
+ query: { qrcode: loginSessionId },
427
+ baseUrl: pollHost,
428
+ });
429
+ if (res.status === 'scaned_but_redirect' && res.redirect_host) {
430
+ qrStatusHosts.set(loginSessionId, normalizeIlinkHost(res.redirect_host));
431
+ }
432
+ if (res.status === 'confirmed' && res.bot_token && res.ilink_bot_id) {
433
+ qrStatusHosts.delete(loginSessionId);
434
+ return {
435
+ linked: true,
436
+ status: res.status,
437
+ credentials: {
438
+ accountId: res.ilink_bot_id,
439
+ token: res.bot_token,
440
+ baseUrl: res.baseurl ? normalizeIlinkHost(res.baseurl) : qrBaseUrl,
441
+ },
442
+ };
443
+ }
444
+ // `need_verifycode`/`verify_code_blocked` have no UI path yet (see
445
+ // module doc comment) — surface as "still not linked" so the caller's
446
+ // own attempt-timeout eventually reports back to the user, rather than
447
+ // silently hanging on a status this client can't act on.
448
+ return { linked: false, status: res.status };
449
+ },
450
+ async getUpdates(creds, _timeoutSeconds) {
451
+ const cursor = updateCursors.get(creds.accountId) ?? '';
452
+ const res = await ilinkFetch({
453
+ method: 'POST',
454
+ path: '/ilink/bot/getupdates',
455
+ body: { get_updates_buf: cursor, base_info: clientBaseInfo() },
456
+ botToken: creds.token,
457
+ baseUrl: creds.baseUrl,
458
+ // iLink's own long-poll legitimately blocks up to _timeoutSeconds —
459
+ // give it headroom above that instead of the 15s default meant for
460
+ // quick request/response calls.
461
+ timeoutMs: _timeoutSeconds * 1000 + 10000,
462
+ });
463
+ if (res.get_updates_buf !== undefined)
464
+ updateCursors.set(creds.accountId, res.get_updates_buf);
465
+ // `ret` is OMITTED entirely on a successful response (confirmed live
466
+ // 2026-09-10 — every real getupdates success we captured had no `ret`
467
+ // field at all, just `msgs`/`get_updates_buf`), matching protocol.md's
468
+ // explicit rule for sendMessage ("an absent ret does not trigger this
469
+ // check") which applies the same way here. The old `res.ret !== 0`
470
+ // check was true for `undefined` too, so it threw "ret=undefined" on
471
+ // every single successful call — the actual reason messages we KNEW
472
+ // existed (confirmed via a raw curl to the same account) never made it
473
+ // through this client.
474
+ if (res.ret !== undefined && res.ret !== 0) {
475
+ throw new Error(`iLink getupdates failed: ret=${res.ret} errmsg=${res.errmsg ?? 'unknown'}`);
476
+ }
477
+ return (res.msgs ?? [])
478
+ // message_type 2 = the bot's own messages echoed back — never a real
479
+ // inbound message, so treating them as one would make the agent
480
+ // reply to itself.
481
+ .filter((m) => m.message_type === 1)
482
+ .map((m) => {
483
+ let image;
484
+ let file;
485
+ try {
486
+ image = resolveWeixinImageRef(m.item_list);
487
+ }
488
+ catch {
489
+ // A malformed item or a non-allowlisted full_url (SSRF guard)
490
+ // must not drop the whole batch — this one message just arrives
491
+ // without its image, same as any other undeliverable-media case.
492
+ image = undefined;
493
+ }
494
+ try {
495
+ file = resolveWeixinFileRef(m.item_list);
496
+ }
497
+ catch {
498
+ file = undefined;
499
+ }
500
+ return {
501
+ id: m.message_id,
502
+ fromId: m.from_user_id,
503
+ text: textFromItems(m.item_list),
504
+ reply: resolveWeixinReply(m.item_list),
505
+ timestamp: m.create_time_ms,
506
+ contextToken: m.context_token,
507
+ image,
508
+ file,
509
+ };
510
+ });
511
+ },
512
+ async sendText(creds, toId, text, contextToken) {
513
+ const res = await ilinkFetch({
514
+ method: 'POST',
515
+ path: '/ilink/bot/sendmessage',
516
+ body: {
517
+ msg: {
518
+ from_user_id: '',
519
+ to_user_id: toId,
520
+ // A fixed prefix, not resolvedBotAgent: this is just a dedup/
521
+ // idempotency token, not user-facing, and a deployer's own
522
+ // botAgent value isn't guaranteed to be ID-safe (arbitrary
523
+ // characters, unlike the sanitized UA-style string protocol.md
524
+ // describes for bot_agent itself, which this gateway doesn't
525
+ // enforce on its own botAgent config field).
526
+ client_id: `${DEFAULT_BOT_AGENT}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`,
527
+ message_type: 2,
528
+ message_state: 2,
529
+ context_token: contextToken ?? '',
530
+ item_list: [{ type: 1, text_item: { text } }],
531
+ },
532
+ base_info: clientBaseInfo(),
533
+ },
534
+ botToken: creds.token,
535
+ baseUrl: creds.baseUrl,
536
+ });
537
+ // Same fix as getUpdates above — confirmed live: a successful
538
+ // sendmessage response is just `{"message_id": ...}`, no `ret` field
539
+ // at all. Per protocol.md: "A non-zero ret throws; an absent ret does
540
+ // not trigger this check."
541
+ if (res.ret !== undefined && res.ret !== 0) {
542
+ throw new Error(`iLink sendmessage failed: ret=${res.ret} errmsg=${res.errmsg ?? 'unknown'}`);
543
+ }
544
+ },
545
+ async notifyStart(creds) {
546
+ await ilinkFetch({
547
+ method: 'POST',
548
+ path: '/ilink/bot/msg/notifystart',
549
+ body: { base_info: clientBaseInfo() },
550
+ botToken: creds.token,
551
+ baseUrl: creds.baseUrl,
552
+ timeoutMs: 10000,
553
+ });
554
+ },
555
+ async notifyStop(creds) {
556
+ await ilinkFetch({
557
+ method: 'POST',
558
+ path: '/ilink/bot/msg/notifystop',
559
+ body: { base_info: clientBaseInfo() },
560
+ botToken: creds.token,
561
+ baseUrl: creds.baseUrl,
562
+ timeoutMs: 10000,
563
+ });
564
+ },
565
+ };
566
+ }
567
+ /** A deterministic checkerboard SVG, styled to read as "a QR code" at a glance
568
+ * without a real QR-encoding dependency — this fake client never needs to be
569
+ * actually scanned. */
570
+ function fakeQrDataUri(seed) {
571
+ const size = 21;
572
+ let hash = 0;
573
+ for (let i = 0; i < seed.length; i++)
574
+ hash = (hash * 31 + seed.charCodeAt(i)) >>> 0;
575
+ let cells = '';
576
+ for (let y = 0; y < size; y++) {
577
+ for (let x = 0; x < size; x++) {
578
+ // Corner "finder" squares, like a real QR code, plus a pseudo-random fill
579
+ // elsewhere so every fake session looks visually distinct.
580
+ const inFinder = (x < 7 && y < 7) || (x >= size - 7 && y < 7) || (x < 7 && y >= size - 7);
581
+ hash = (hash * 1103515245 + 12345) >>> 0;
582
+ const on = inFinder ? (x % 6 !== 3 && y % 6 !== 3) : hash % 2 === 0;
583
+ if (on)
584
+ cells += `<rect x="${x}" y="${y}" width="1" height="1"/>`;
585
+ }
586
+ }
587
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${size} ${size}" fill="#000">` +
588
+ `<rect width="${size}" height="${size}" fill="#fff"/>${cells}</svg>`;
589
+ return `data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')}`;
590
+ }
591
+ /**
592
+ * Local-testing-only fake client — no network calls, no real iLink account
593
+ * needed. Simulates a QR scan completing after a few polls, then occasionally
594
+ * delivers one fake inbound message (carrying its own fake `contextToken`,
595
+ * exactly like the real protocol) so the pending-sender/access-control UI
596
+ * has something to show. Never wired up by default — see
597
+ * `isWeChatILinkFakeEnabled()` and its call site in AgentRunner.
598
+ *
599
+ * NOT a substitute for the real API contract — exists purely so the apps/web
600
+ * card's UI/UX (QR render, link→linked transition, DM allowlist, disconnect)
601
+ * can be manually verified end-to-end without a real WeChat account.
602
+ */
603
+ function createFakeILinkClient() {
604
+ let pollCount = 0;
605
+ let updateCount = 0;
606
+ return {
607
+ async requestLinkQr() {
608
+ pollCount = 0;
609
+ const loginSessionId = `fake-session-${Date.now()}`;
610
+ return { qrDataUri: fakeQrDataUri(loginSessionId), loginSessionId };
611
+ },
612
+ async pollLinkStatus(loginSessionId) {
613
+ pollCount += 1;
614
+ // ~3 polls at the manager's 2s interval ⇒ linked after ~6s, long enough
615
+ // to see the QR/"Waiting for scan…" state before it resolves.
616
+ if (pollCount < 3)
617
+ return { linked: false };
618
+ return {
619
+ linked: true,
620
+ credentials: {
621
+ accountId: 'fake-account',
622
+ token: `fake-token-${loginSessionId}`,
623
+ baseUrl: 'fake://local-test',
624
+ },
625
+ };
626
+ },
627
+ async getUpdates(_creds, timeoutSeconds) {
628
+ // Real iLink blocks up to `timeoutSeconds`; a short fixed sleep here
629
+ // keeps manual testing responsive instead of waiting the full 35s.
630
+ await new Promise((resolve) => setTimeout(resolve, Math.min(timeoutSeconds, 4) * 1000));
631
+ updateCount += 1;
632
+ // One fake message shortly after linking, then quiet — enough to
633
+ // exercise the pending-sender/allowlist flow without spamming.
634
+ if (updateCount === 2) {
635
+ return [
636
+ {
637
+ id: `fake-msg-${Date.now()}`,
638
+ fromId: 'fake-tester',
639
+ text: 'Hi, this is a fake WeChat test message — approve me to keep chatting!',
640
+ displayName: 'Fake Tester',
641
+ timestamp: Date.now(),
642
+ contextToken: 'fake-context-token',
643
+ },
644
+ ];
645
+ }
646
+ return [];
647
+ },
648
+ async sendText(_creds, toId, text, contextToken) {
649
+ // eslint-disable-next-line no-console -- local-testing-only visibility, not production logging
650
+ console.log(`[fake-ilink] would send to ${toId} (ctx=${contextToken ?? 'none'}): ${text}`);
651
+ },
652
+ async notifyStart() { },
653
+ async notifyStop() { },
654
+ };
655
+ }
656
+ /** Opt-in only, local dev/manual-testing use — see createFakeILinkClient's doc comment. */
657
+ function isWeChatILinkFakeEnabled() {
658
+ return process.env.WECHAT_ILINK_FAKE === 'true';
659
+ }
660
+ //# sourceMappingURL=ilink-client.js.map