@0xmaxma/claude-gateway 1.8.14 → 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 (582) hide show
  1. package/README.md +28 -1613
  2. package/config.template.json +7 -1
  3. package/dist/agent/runner.d.ts +377 -0
  4. package/dist/agent/runner.d.ts.map +1 -1
  5. package/dist/agent/runner.js +680 -24
  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 +135 -10
  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/apps/agent-container-migration.d.ts +11 -0
  52. package/dist/apps/agent-container-migration.d.ts.map +1 -0
  53. package/dist/apps/agent-container-migration.js +85 -0
  54. package/dist/apps/agent-container-migration.js.map +1 -0
  55. package/dist/apps/installer.d.ts +1 -1
  56. package/dist/apps/installer.d.ts.map +1 -1
  57. package/dist/apps/installer.js +2 -1
  58. package/dist/apps/installer.js.map +1 -1
  59. package/dist/cli/commands/tasks.d.ts +5 -0
  60. package/dist/cli/commands/tasks.d.ts.map +1 -0
  61. package/dist/cli/commands/tasks.js +92 -0
  62. package/dist/cli/commands/tasks.js.map +1 -0
  63. package/dist/cli/http-client.d.ts +2 -0
  64. package/dist/cli/http-client.d.ts.map +1 -1
  65. package/dist/cli/http-client.js +8 -3
  66. package/dist/cli/http-client.js.map +1 -1
  67. package/dist/cli/index.d.ts.map +1 -1
  68. package/dist/cli/index.js +5 -1
  69. package/dist/cli/index.js.map +1 -1
  70. package/dist/config/config-write-lock.d.ts +2 -0
  71. package/dist/config/config-write-lock.d.ts.map +1 -1
  72. package/dist/config/config-write-lock.js +5 -0
  73. package/dist/config/config-write-lock.js.map +1 -1
  74. package/dist/config/loader.d.ts.map +1 -1
  75. package/dist/config/loader.js +87 -2
  76. package/dist/config/loader.js.map +1 -1
  77. package/dist/config/migrator.d.ts.map +1 -1
  78. package/dist/config/migrator.js +12 -1
  79. package/dist/config/migrator.js.map +1 -1
  80. package/dist/config/watcher.d.ts.map +1 -1
  81. package/dist/config/watcher.js +6 -0
  82. package/dist/config/watcher.js.map +1 -1
  83. package/dist/connectors/resolve.d.ts.map +1 -1
  84. package/dist/connectors/resolve.js +3 -1
  85. package/dist/connectors/resolve.js.map +1 -1
  86. package/dist/connectors/types.d.ts +8 -0
  87. package/dist/connectors/types.d.ts.map +1 -1
  88. package/dist/cron/manager.d.ts.map +1 -1
  89. package/dist/cron/manager.js +21 -2
  90. package/dist/cron/manager.js.map +1 -1
  91. package/dist/discord/receiver.d.ts +2 -1
  92. package/dist/discord/receiver.d.ts.map +1 -1
  93. package/dist/discord/receiver.js +5 -1
  94. package/dist/discord/receiver.js.map +1 -1
  95. package/dist/history/db.d.ts +3 -0
  96. package/dist/history/db.d.ts.map +1 -1
  97. package/dist/history/db.js +21 -1
  98. package/dist/history/db.js.map +1 -1
  99. package/dist/history/types.d.ts +2 -0
  100. package/dist/history/types.d.ts.map +1 -1
  101. package/dist/index.js +48 -9
  102. package/dist/index.js.map +1 -1
  103. package/dist/orchestration/bounded-queue.d.ts +15 -0
  104. package/dist/orchestration/bounded-queue.d.ts.map +1 -0
  105. package/dist/orchestration/bounded-queue.js +58 -0
  106. package/dist/orchestration/bounded-queue.js.map +1 -0
  107. package/dist/orchestration/bridge.d.ts +55 -0
  108. package/dist/orchestration/bridge.d.ts.map +1 -0
  109. package/dist/orchestration/bridge.js +234 -0
  110. package/dist/orchestration/bridge.js.map +1 -0
  111. package/dist/orchestration/browser-routing.d.ts +7 -0
  112. package/dist/orchestration/browser-routing.d.ts.map +1 -0
  113. package/dist/orchestration/browser-routing.js +22 -0
  114. package/dist/orchestration/browser-routing.js.map +1 -0
  115. package/dist/orchestration/capabilities.d.ts +49 -0
  116. package/dist/orchestration/capabilities.d.ts.map +1 -0
  117. package/dist/orchestration/capabilities.js +417 -0
  118. package/dist/orchestration/capabilities.js.map +1 -0
  119. package/dist/orchestration/capacity.d.ts +14 -0
  120. package/dist/orchestration/capacity.d.ts.map +1 -0
  121. package/dist/orchestration/capacity.js +52 -0
  122. package/dist/orchestration/capacity.js.map +1 -0
  123. package/dist/orchestration/channel-activity.d.ts +21 -0
  124. package/dist/orchestration/channel-activity.d.ts.map +1 -0
  125. package/dist/orchestration/channel-activity.js +72 -0
  126. package/dist/orchestration/channel-activity.js.map +1 -0
  127. package/dist/orchestration/channel-controls.d.ts +32 -0
  128. package/dist/orchestration/channel-controls.d.ts.map +1 -0
  129. package/dist/orchestration/channel-controls.js +105 -0
  130. package/dist/orchestration/channel-controls.js.map +1 -0
  131. package/dist/orchestration/channel-input-media.d.ts +11 -0
  132. package/dist/orchestration/channel-input-media.d.ts.map +1 -0
  133. package/dist/orchestration/channel-input-media.js +54 -0
  134. package/dist/orchestration/channel-input-media.js.map +1 -0
  135. package/dist/orchestration/channel-media.d.ts +4 -0
  136. package/dist/orchestration/channel-media.d.ts.map +1 -0
  137. package/dist/orchestration/channel-media.js +70 -0
  138. package/dist/orchestration/channel-media.js.map +1 -0
  139. package/dist/orchestration/channel-speech.d.ts +7 -0
  140. package/dist/orchestration/channel-speech.d.ts.map +1 -0
  141. package/dist/orchestration/channel-speech.js +65 -0
  142. package/dist/orchestration/channel-speech.js.map +1 -0
  143. package/dist/orchestration/cli-skills.d.ts +12 -0
  144. package/dist/orchestration/cli-skills.d.ts.map +1 -0
  145. package/dist/orchestration/cli-skills.js +132 -0
  146. package/dist/orchestration/cli-skills.js.map +1 -0
  147. package/dist/orchestration/config.d.ts +256 -0
  148. package/dist/orchestration/config.d.ts.map +1 -0
  149. package/dist/orchestration/config.js +110 -0
  150. package/dist/orchestration/config.js.map +1 -0
  151. package/dist/orchestration/container.d.ts +21 -0
  152. package/dist/orchestration/container.d.ts.map +1 -0
  153. package/dist/orchestration/container.js +195 -0
  154. package/dist/orchestration/container.js.map +1 -0
  155. package/dist/orchestration/control-delivery.d.ts +5 -0
  156. package/dist/orchestration/control-delivery.d.ts.map +1 -0
  157. package/dist/orchestration/control-delivery.js +50 -0
  158. package/dist/orchestration/control-delivery.js.map +1 -0
  159. package/dist/orchestration/conversation-intake.d.ts +32 -0
  160. package/dist/orchestration/conversation-intake.d.ts.map +1 -0
  161. package/dist/orchestration/conversation-intake.js +92 -0
  162. package/dist/orchestration/conversation-intake.js.map +1 -0
  163. package/dist/orchestration/decisions.d.ts +27 -0
  164. package/dist/orchestration/decisions.d.ts.map +1 -0
  165. package/dist/orchestration/decisions.js +148 -0
  166. package/dist/orchestration/decisions.js.map +1 -0
  167. package/dist/orchestration/delivery.d.ts +30 -0
  168. package/dist/orchestration/delivery.d.ts.map +1 -0
  169. package/dist/orchestration/delivery.js +219 -0
  170. package/dist/orchestration/delivery.js.map +1 -0
  171. package/dist/orchestration/display-stream.d.ts +4 -0
  172. package/dist/orchestration/display-stream.d.ts.map +1 -0
  173. package/dist/orchestration/display-stream.js +77 -0
  174. package/dist/orchestration/display-stream.js.map +1 -0
  175. package/dist/orchestration/events.d.ts +30 -0
  176. package/dist/orchestration/events.d.ts.map +1 -0
  177. package/dist/orchestration/events.js +65 -0
  178. package/dist/orchestration/events.js.map +1 -0
  179. package/dist/orchestration/execution-observation.d.ts +38 -0
  180. package/dist/orchestration/execution-observation.d.ts.map +1 -0
  181. package/dist/orchestration/execution-observation.js +25 -0
  182. package/dist/orchestration/execution-observation.js.map +1 -0
  183. package/dist/orchestration/file-delivery.d.ts +16 -0
  184. package/dist/orchestration/file-delivery.d.ts.map +1 -0
  185. package/dist/orchestration/file-delivery.js +139 -0
  186. package/dist/orchestration/file-delivery.js.map +1 -0
  187. package/dist/orchestration/gateway-config.d.ts +17 -0
  188. package/dist/orchestration/gateway-config.d.ts.map +1 -0
  189. package/dist/orchestration/gateway-config.js +128 -0
  190. package/dist/orchestration/gateway-config.js.map +1 -0
  191. package/dist/orchestration/history.d.ts +13 -0
  192. package/dist/orchestration/history.d.ts.map +1 -0
  193. package/dist/orchestration/history.js +48 -0
  194. package/dist/orchestration/history.js.map +1 -0
  195. package/dist/orchestration/identity.d.ts +6 -0
  196. package/dist/orchestration/identity.d.ts.map +1 -0
  197. package/dist/orchestration/identity.js +13 -0
  198. package/dist/orchestration/identity.js.map +1 -0
  199. package/dist/orchestration/inference-errors.d.ts +3 -0
  200. package/dist/orchestration/inference-errors.d.ts.map +1 -0
  201. package/dist/orchestration/inference-errors.js +16 -0
  202. package/dist/orchestration/inference-errors.js.map +1 -0
  203. package/dist/orchestration/input-images.d.ts +12 -0
  204. package/dist/orchestration/input-images.d.ts.map +1 -0
  205. package/dist/orchestration/input-images.js +66 -0
  206. package/dist/orchestration/input-images.js.map +1 -0
  207. package/dist/orchestration/instance-lock.d.ts +5 -0
  208. package/dist/orchestration/instance-lock.d.ts.map +1 -0
  209. package/dist/orchestration/instance-lock.js +33 -0
  210. package/dist/orchestration/instance-lock.js.map +1 -0
  211. package/dist/orchestration/line-loading.d.ts +21 -0
  212. package/dist/orchestration/line-loading.d.ts.map +1 -0
  213. package/dist/orchestration/line-loading.js +89 -0
  214. package/dist/orchestration/line-loading.js.map +1 -0
  215. package/dist/orchestration/media.d.ts +3 -0
  216. package/dist/orchestration/media.d.ts.map +1 -0
  217. package/dist/orchestration/media.js +46 -0
  218. package/dist/orchestration/media.js.map +1 -0
  219. package/dist/orchestration/migrations/schema.d.ts +3 -0
  220. package/dist/orchestration/migrations/schema.d.ts.map +1 -0
  221. package/dist/orchestration/migrations/schema.js +80 -0
  222. package/dist/orchestration/migrations/schema.js.map +1 -0
  223. package/dist/orchestration/notification-mailbox.d.ts +5 -0
  224. package/dist/orchestration/notification-mailbox.d.ts.map +1 -0
  225. package/dist/orchestration/notification-mailbox.js +29 -0
  226. package/dist/orchestration/notification-mailbox.js.map +1 -0
  227. package/dist/orchestration/process-activity.d.ts +23 -0
  228. package/dist/orchestration/process-activity.d.ts.map +1 -0
  229. package/dist/orchestration/process-activity.js +95 -0
  230. package/dist/orchestration/process-activity.js.map +1 -0
  231. package/dist/orchestration/process-supervisor.d.ts +15 -0
  232. package/dist/orchestration/process-supervisor.d.ts.map +1 -0
  233. package/dist/orchestration/process-supervisor.js +92 -0
  234. package/dist/orchestration/process-supervisor.js.map +1 -0
  235. package/dist/orchestration/process-turn.d.ts +35 -0
  236. package/dist/orchestration/process-turn.d.ts.map +1 -0
  237. package/dist/orchestration/process-turn.js +246 -0
  238. package/dist/orchestration/process-turn.js.map +1 -0
  239. package/dist/orchestration/progress-review.d.ts +28 -0
  240. package/dist/orchestration/progress-review.d.ts.map +1 -0
  241. package/dist/orchestration/progress-review.js +48 -0
  242. package/dist/orchestration/progress-review.js.map +1 -0
  243. package/dist/orchestration/recovery.d.ts +8 -0
  244. package/dist/orchestration/recovery.d.ts.map +1 -0
  245. package/dist/orchestration/recovery.js +35 -0
  246. package/dist/orchestration/recovery.js.map +1 -0
  247. package/dist/orchestration/reply-context.d.ts +7 -0
  248. package/dist/orchestration/reply-context.d.ts.map +1 -0
  249. package/dist/orchestration/reply-context.js +54 -0
  250. package/dist/orchestration/reply-context.js.map +1 -0
  251. package/dist/orchestration/runtime.d.ts +232 -0
  252. package/dist/orchestration/runtime.d.ts.map +1 -0
  253. package/dist/orchestration/runtime.js +962 -0
  254. package/dist/orchestration/runtime.js.map +1 -0
  255. package/dist/orchestration/skills.d.ts +17 -0
  256. package/dist/orchestration/skills.d.ts.map +1 -0
  257. package/dist/orchestration/skills.js +32 -0
  258. package/dist/orchestration/skills.js.map +1 -0
  259. package/dist/orchestration/source-policy.d.ts +6 -0
  260. package/dist/orchestration/source-policy.d.ts.map +1 -0
  261. package/dist/orchestration/source-policy.js +16 -0
  262. package/dist/orchestration/source-policy.js.map +1 -0
  263. package/dist/orchestration/speech.d.ts +24 -0
  264. package/dist/orchestration/speech.d.ts.map +1 -0
  265. package/dist/orchestration/speech.js +61 -0
  266. package/dist/orchestration/speech.js.map +1 -0
  267. package/dist/orchestration/stop-controls.d.ts +30 -0
  268. package/dist/orchestration/stop-controls.d.ts.map +1 -0
  269. package/dist/orchestration/stop-controls.js +79 -0
  270. package/dist/orchestration/stop-controls.js.map +1 -0
  271. package/dist/orchestration/store.d.ts +86 -0
  272. package/dist/orchestration/store.d.ts.map +1 -0
  273. package/dist/orchestration/store.js +275 -0
  274. package/dist/orchestration/store.js.map +1 -0
  275. package/dist/orchestration/task-controls.d.ts +130 -0
  276. package/dist/orchestration/task-controls.d.ts.map +1 -0
  277. package/dist/orchestration/task-controls.js +55 -0
  278. package/dist/orchestration/task-controls.js.map +1 -0
  279. package/dist/orchestration/task-files.d.ts +29 -0
  280. package/dist/orchestration/task-files.d.ts.map +1 -0
  281. package/dist/orchestration/task-files.js +200 -0
  282. package/dist/orchestration/task-files.js.map +1 -0
  283. package/dist/orchestration/task-report.d.ts +7 -0
  284. package/dist/orchestration/task-report.d.ts.map +1 -0
  285. package/dist/orchestration/task-report.js +33 -0
  286. package/dist/orchestration/task-report.js.map +1 -0
  287. package/dist/orchestration/tasks/checkpoint-hook.d.ts +19 -0
  288. package/dist/orchestration/tasks/checkpoint-hook.d.ts.map +1 -0
  289. package/dist/orchestration/tasks/checkpoint-hook.js +49 -0
  290. package/dist/orchestration/tasks/checkpoint-hook.js.map +1 -0
  291. package/dist/orchestration/tasks/cleanup.d.ts +18 -0
  292. package/dist/orchestration/tasks/cleanup.d.ts.map +1 -0
  293. package/dist/orchestration/tasks/cleanup.js +113 -0
  294. package/dist/orchestration/tasks/cleanup.js.map +1 -0
  295. package/dist/orchestration/tasks/driver.d.ts +23 -0
  296. package/dist/orchestration/tasks/driver.d.ts.map +1 -0
  297. package/dist/orchestration/tasks/driver.js +209 -0
  298. package/dist/orchestration/tasks/driver.js.map +1 -0
  299. package/dist/orchestration/tasks/failure.d.ts +5 -0
  300. package/dist/orchestration/tasks/failure.d.ts.map +1 -0
  301. package/dist/orchestration/tasks/failure.js +17 -0
  302. package/dist/orchestration/tasks/failure.js.map +1 -0
  303. package/dist/orchestration/tasks/pool.d.ts +18 -0
  304. package/dist/orchestration/tasks/pool.d.ts.map +1 -0
  305. package/dist/orchestration/tasks/pool.js +73 -0
  306. package/dist/orchestration/tasks/pool.js.map +1 -0
  307. package/dist/orchestration/tasks/scheduler.d.ts +38 -0
  308. package/dist/orchestration/tasks/scheduler.d.ts.map +1 -0
  309. package/dist/orchestration/tasks/scheduler.js +160 -0
  310. package/dist/orchestration/tasks/scheduler.js.map +1 -0
  311. package/dist/orchestration/tasks/service.d.ts +109 -0
  312. package/dist/orchestration/tasks/service.d.ts.map +1 -0
  313. package/dist/orchestration/tasks/service.js +697 -0
  314. package/dist/orchestration/tasks/service.js.map +1 -0
  315. package/dist/orchestration/tasks/shared-workspace.d.ts +25 -0
  316. package/dist/orchestration/tasks/shared-workspace.d.ts.map +1 -0
  317. package/dist/orchestration/tasks/shared-workspace.js +162 -0
  318. package/dist/orchestration/tasks/shared-workspace.js.map +1 -0
  319. package/dist/orchestration/tasks/task-directive.d.ts +6 -0
  320. package/dist/orchestration/tasks/task-directive.d.ts.map +1 -0
  321. package/dist/orchestration/tasks/task-directive.js +41 -0
  322. package/dist/orchestration/tasks/task-directive.js.map +1 -0
  323. package/dist/orchestration/tasks/timing.d.ts +15 -0
  324. package/dist/orchestration/tasks/timing.d.ts.map +1 -0
  325. package/dist/orchestration/tasks/timing.js +48 -0
  326. package/dist/orchestration/tasks/timing.js.map +1 -0
  327. package/dist/orchestration/tasks/tool-repetition.d.ts +3 -0
  328. package/dist/orchestration/tasks/tool-repetition.d.ts.map +1 -0
  329. package/dist/orchestration/tasks/tool-repetition.js +30 -0
  330. package/dist/orchestration/tasks/tool-repetition.js.map +1 -0
  331. package/dist/orchestration/tasks/workspace.d.ts +24 -0
  332. package/dist/orchestration/tasks/workspace.d.ts.map +1 -0
  333. package/dist/orchestration/tasks/workspace.js +147 -0
  334. package/dist/orchestration/tasks/workspace.js.map +1 -0
  335. package/dist/orchestration/telegram-speech.d.ts +17 -0
  336. package/dist/orchestration/telegram-speech.d.ts.map +1 -0
  337. package/dist/orchestration/telegram-speech.js +55 -0
  338. package/dist/orchestration/telegram-speech.js.map +1 -0
  339. package/dist/orchestration/telegram-tool-status.d.ts +23 -0
  340. package/dist/orchestration/telegram-tool-status.d.ts.map +1 -0
  341. package/dist/orchestration/telegram-tool-status.js +207 -0
  342. package/dist/orchestration/telegram-tool-status.js.map +1 -0
  343. package/dist/orchestration/telegram-voices.d.ts +42 -0
  344. package/dist/orchestration/telegram-voices.d.ts.map +1 -0
  345. package/dist/orchestration/telegram-voices.js +70 -0
  346. package/dist/orchestration/telegram-voices.js.map +1 -0
  347. package/dist/orchestration/tool-activity.d.ts +10 -0
  348. package/dist/orchestration/tool-activity.d.ts.map +1 -0
  349. package/dist/orchestration/tool-activity.js +38 -0
  350. package/dist/orchestration/tool-activity.js.map +1 -0
  351. package/dist/orchestration/types.d.ts +177 -0
  352. package/dist/orchestration/types.d.ts.map +1 -0
  353. package/dist/orchestration/types.js +13 -0
  354. package/dist/orchestration/types.js.map +1 -0
  355. package/dist/orchestration/voice-reply-policy.d.ts +8 -0
  356. package/dist/orchestration/voice-reply-policy.d.ts.map +1 -0
  357. package/dist/orchestration/voice-reply-policy.js +46 -0
  358. package/dist/orchestration/voice-reply-policy.js.map +1 -0
  359. package/dist/orchestration/worker-shares.d.ts +8 -0
  360. package/dist/orchestration/worker-shares.d.ts.map +1 -0
  361. package/dist/orchestration/worker-shares.js +59 -0
  362. package/dist/orchestration/worker-shares.js.map +1 -0
  363. package/dist/orchestration/workflow.d.ts +121 -0
  364. package/dist/orchestration/workflow.d.ts.map +1 -0
  365. package/dist/orchestration/workflow.js +69 -0
  366. package/dist/orchestration/workflow.js.map +1 -0
  367. package/dist/session/input-image.d.ts +10 -0
  368. package/dist/session/input-image.d.ts.map +1 -0
  369. package/dist/session/input-image.js +3 -0
  370. package/dist/session/input-image.js.map +1 -0
  371. package/dist/session/process.d.ts +15 -3
  372. package/dist/session/process.d.ts.map +1 -1
  373. package/dist/session/process.js +143 -35
  374. package/dist/session/process.js.map +1 -1
  375. package/dist/session/runtime-profile.d.ts +36 -0
  376. package/dist/session/runtime-profile.d.ts.map +1 -0
  377. package/dist/session/runtime-profile.js +63 -0
  378. package/dist/session/runtime-profile.js.map +1 -0
  379. package/dist/session/store.d.ts.map +1 -1
  380. package/dist/session/store.js +23 -1
  381. package/dist/session/store.js.map +1 -1
  382. package/dist/share/share-store.d.ts +3 -1
  383. package/dist/share/share-store.d.ts.map +1 -1
  384. package/dist/share/share-store.js +13 -2
  385. package/dist/share/share-store.js.map +1 -1
  386. package/dist/shared/line-request-order.d.ts +2 -0
  387. package/dist/shared/line-request-order.d.ts.map +1 -0
  388. package/dist/shared/line-request-order.js +16 -0
  389. package/dist/shared/line-request-order.js.map +1 -0
  390. package/dist/shared/task-elapsed.d.ts +3 -0
  391. package/dist/shared/task-elapsed.d.ts.map +1 -0
  392. package/dist/shared/task-elapsed.js +12 -0
  393. package/dist/shared/task-elapsed.js.map +1 -0
  394. package/dist/shared/task-updated-time.d.ts +3 -0
  395. package/dist/shared/task-updated-time.d.ts.map +1 -0
  396. package/dist/shared/task-updated-time.js +15 -0
  397. package/dist/shared/task-updated-time.js.map +1 -0
  398. package/dist/skills/loader.d.ts +2 -0
  399. package/dist/skills/loader.d.ts.map +1 -1
  400. package/dist/skills/loader.js.map +1 -1
  401. package/dist/telegram/chunks.d.ts +19 -0
  402. package/dist/telegram/chunks.d.ts.map +1 -0
  403. package/dist/telegram/chunks.js +129 -0
  404. package/dist/telegram/chunks.js.map +1 -0
  405. package/dist/telegram/markdown.d.ts.map +1 -1
  406. package/dist/telegram/markdown.js +18 -3
  407. package/dist/telegram/markdown.js.map +1 -1
  408. package/dist/telegram/receiver.d.ts +2 -1
  409. package/dist/telegram/receiver.d.ts.map +1 -1
  410. package/dist/telegram/receiver.js +6 -1
  411. package/dist/telegram/receiver.js.map +1 -1
  412. package/dist/types.d.ts +14 -0
  413. package/dist/types.d.ts.map +1 -1
  414. package/dist/ui/web-ui.d.ts.map +1 -1
  415. package/dist/ui/web-ui.js +31 -5
  416. package/dist/ui/web-ui.js.map +1 -1
  417. package/dist/voice/diagnostics.d.ts +29 -0
  418. package/dist/voice/diagnostics.d.ts.map +1 -0
  419. package/dist/voice/diagnostics.js +37 -0
  420. package/dist/voice/diagnostics.js.map +1 -0
  421. package/dist/voice/errors.d.ts +19 -0
  422. package/dist/voice/errors.d.ts.map +1 -0
  423. package/dist/voice/errors.js +133 -0
  424. package/dist/voice/errors.js.map +1 -0
  425. package/dist/voice/line-audio.d.ts +4 -0
  426. package/dist/voice/line-audio.d.ts.map +1 -0
  427. package/dist/voice/line-audio.js +27 -0
  428. package/dist/voice/line-audio.js.map +1 -0
  429. package/dist/voice/managed-quota.d.ts +5 -0
  430. package/dist/voice/managed-quota.d.ts.map +1 -0
  431. package/dist/voice/managed-quota.js +35 -0
  432. package/dist/voice/managed-quota.js.map +1 -0
  433. package/dist/voice/mp3.d.ts +3 -0
  434. package/dist/voice/mp3.d.ts.map +1 -0
  435. package/dist/voice/mp3.js +30 -0
  436. package/dist/voice/mp3.js.map +1 -0
  437. package/dist/voice/notes.d.ts +9 -0
  438. package/dist/voice/notes.d.ts.map +1 -0
  439. package/dist/voice/notes.js +68 -0
  440. package/dist/voice/notes.js.map +1 -0
  441. package/dist/voice/playback.d.ts +22 -0
  442. package/dist/voice/playback.d.ts.map +1 -0
  443. package/dist/voice/playback.js +52 -0
  444. package/dist/voice/playback.js.map +1 -0
  445. package/dist/voice/preview.d.ts +6 -0
  446. package/dist/voice/preview.d.ts.map +1 -0
  447. package/dist/voice/preview.js +44 -0
  448. package/dist/voice/preview.js.map +1 -0
  449. package/dist/voice/protocol.d.ts +10 -0
  450. package/dist/voice/protocol.d.ts.map +1 -0
  451. package/dist/voice/protocol.js +30 -0
  452. package/dist/voice/protocol.js.map +1 -0
  453. package/dist/voice/providers/cartesia-tts.d.ts +26 -0
  454. package/dist/voice/providers/cartesia-tts.d.ts.map +1 -0
  455. package/dist/voice/providers/cartesia-tts.js +95 -0
  456. package/dist/voice/providers/cartesia-tts.js.map +1 -0
  457. package/dist/voice/providers/deepgram-stt.d.ts +20 -0
  458. package/dist/voice/providers/deepgram-stt.d.ts.map +1 -0
  459. package/dist/voice/providers/deepgram-stt.js +66 -0
  460. package/dist/voice/providers/deepgram-stt.js.map +1 -0
  461. package/dist/voice/providers/elevenlabs-stt.d.ts +20 -0
  462. package/dist/voice/providers/elevenlabs-stt.d.ts.map +1 -0
  463. package/dist/voice/providers/elevenlabs-stt.js +92 -0
  464. package/dist/voice/providers/elevenlabs-stt.js.map +1 -0
  465. package/dist/voice/providers/elevenlabs-tts.d.ts +26 -0
  466. package/dist/voice/providers/elevenlabs-tts.d.ts.map +1 -0
  467. package/dist/voice/providers/elevenlabs-tts.js +82 -0
  468. package/dist/voice/providers/elevenlabs-tts.js.map +1 -0
  469. package/dist/voice/providers/encoded-audio-tts.d.ts +21 -0
  470. package/dist/voice/providers/encoded-audio-tts.d.ts.map +1 -0
  471. package/dist/voice/providers/encoded-audio-tts.js +175 -0
  472. package/dist/voice/providers/encoded-audio-tts.js.map +1 -0
  473. package/dist/voice/providers/fake.d.ts +39 -0
  474. package/dist/voice/providers/fake.d.ts.map +1 -0
  475. package/dist/voice/providers/fake.js +41 -0
  476. package/dist/voice/providers/fake.js.map +1 -0
  477. package/dist/voice/providers/gemini-live-stt.d.ts +21 -0
  478. package/dist/voice/providers/gemini-live-stt.d.ts.map +1 -0
  479. package/dist/voice/providers/gemini-live-stt.js +158 -0
  480. package/dist/voice/providers/gemini-live-stt.js.map +1 -0
  481. package/dist/voice/providers/gemini.d.ts +60 -0
  482. package/dist/voice/providers/gemini.d.ts.map +1 -0
  483. package/dist/voice/providers/gemini.js +124 -0
  484. package/dist/voice/providers/gemini.js.map +1 -0
  485. package/dist/voice/providers/model-ref.d.ts +9 -0
  486. package/dist/voice/providers/model-ref.d.ts.map +1 -0
  487. package/dist/voice/providers/model-ref.js +31 -0
  488. package/dist/voice/providers/model-ref.js.map +1 -0
  489. package/dist/voice/providers/openrouter.d.ts +39 -0
  490. package/dist/voice/providers/openrouter.d.ts.map +1 -0
  491. package/dist/voice/providers/openrouter.js +154 -0
  492. package/dist/voice/providers/openrouter.js.map +1 -0
  493. package/dist/voice/providers/paxalabs-stt.d.ts +12 -0
  494. package/dist/voice/providers/paxalabs-stt.d.ts.map +1 -0
  495. package/dist/voice/providers/paxalabs-stt.js +41 -0
  496. package/dist/voice/providers/paxalabs-stt.js.map +1 -0
  497. package/dist/voice/providers/paxalabs-tts.d.ts +23 -0
  498. package/dist/voice/providers/paxalabs-tts.d.ts.map +1 -0
  499. package/dist/voice/providers/paxalabs-tts.js +80 -0
  500. package/dist/voice/providers/paxalabs-tts.js.map +1 -0
  501. package/dist/voice/providers/recorded-stt.d.ts +34 -0
  502. package/dist/voice/providers/recorded-stt.d.ts.map +1 -0
  503. package/dist/voice/providers/recorded-stt.js +59 -0
  504. package/dist/voice/providers/recorded-stt.js.map +1 -0
  505. package/dist/voice/providers/registry.d.ts +10 -0
  506. package/dist/voice/providers/registry.d.ts.map +1 -0
  507. package/dist/voice/providers/registry.js +77 -0
  508. package/dist/voice/providers/registry.js.map +1 -0
  509. package/dist/voice/providers/socket.d.ts +10 -0
  510. package/dist/voice/providers/socket.d.ts.map +1 -0
  511. package/dist/voice/providers/socket.js +91 -0
  512. package/dist/voice/providers/socket.js.map +1 -0
  513. package/dist/voice/providers/upstream.d.ts +9 -0
  514. package/dist/voice/providers/upstream.d.ts.map +1 -0
  515. package/dist/voice/providers/upstream.js +51 -0
  516. package/dist/voice/providers/upstream.js.map +1 -0
  517. package/dist/voice/providers/voice-catalog.d.ts +17 -0
  518. package/dist/voice/providers/voice-catalog.d.ts.map +1 -0
  519. package/dist/voice/providers/voice-catalog.js +167 -0
  520. package/dist/voice/providers/voice-catalog.js.map +1 -0
  521. package/dist/voice/queue.d.ts +2 -0
  522. package/dist/voice/queue.d.ts.map +1 -0
  523. package/dist/voice/queue.js +6 -0
  524. package/dist/voice/queue.js.map +1 -0
  525. package/dist/voice/session.d.ts +86 -0
  526. package/dist/voice/session.d.ts.map +1 -0
  527. package/dist/voice/session.js +464 -0
  528. package/dist/voice/session.js.map +1 -0
  529. package/dist/voice/turn-manager.d.ts +27 -0
  530. package/dist/voice/turn-manager.d.ts.map +1 -0
  531. package/dist/voice/turn-manager.js +75 -0
  532. package/dist/voice/turn-manager.js.map +1 -0
  533. package/dist/voice/types.d.ts +78 -0
  534. package/dist/voice/types.d.ts.map +1 -0
  535. package/dist/voice/types.js +18 -0
  536. package/dist/voice/types.js.map +1 -0
  537. package/dist/voice/wav.d.ts +2 -0
  538. package/dist/voice/wav.d.ts.map +1 -0
  539. package/dist/voice/wav.js +21 -0
  540. package/dist/voice/wav.js.map +1 -0
  541. package/dist/wechat/ilink-client.d.ts +13 -0
  542. package/dist/wechat/ilink-client.d.ts.map +1 -1
  543. package/dist/wechat/ilink-client.js +19 -0
  544. package/dist/wechat/ilink-client.js.map +1 -1
  545. package/dist/whatsapp/manager.d.ts.map +1 -1
  546. package/dist/whatsapp/manager.js +51 -1
  547. package/dist/whatsapp/manager.js.map +1 -1
  548. package/mcp/capability-catalog.ts +206 -0
  549. package/mcp/modules.ts +56 -0
  550. package/mcp/server.ts +18 -37
  551. package/mcp/tools/browser/module.ts +1 -1
  552. package/mcp/tools/browser/skills/open-browser/SKILL.md +5 -3
  553. package/mcp/tools/discord/commands.ts +17 -1
  554. package/mcp/tools/discord/inbound.ts +11 -0
  555. package/mcp/tools/discord/module.ts +42 -2
  556. package/mcp/tools/discord/receiver-server.ts +10 -0
  557. package/mcp/tools/discord/types.ts +4 -1
  558. package/mcp/tools/image/module.ts +3 -1
  559. package/mcp/tools/receiver-spool.test.ts +28 -0
  560. package/mcp/tools/receiver-spool.ts +56 -0
  561. package/mcp/tools/share-file/module.ts +9 -1
  562. package/mcp/tools/shared/share-client.ts +19 -0
  563. package/mcp/tools/tasks/module.ts +35 -0
  564. package/mcp/tools/telegram/media-group.ts +21 -0
  565. package/mcp/tools/telegram/receiver-server.ts +232 -11
  566. package/mcp/tools/telegram/selection-confirmation.ts +20 -0
  567. package/mcp/tools/telegram/task-browser.ts +117 -0
  568. package/mcp/tools/telegram/task-detail.ts +36 -0
  569. package/mcp/tools/telegram/task-elapsed.ts +1 -0
  570. package/mcp/tools/telegram/typing.ts +6 -116
  571. package/mcp/tools/video/module.ts +1 -1
  572. package/mcp/types.ts +5 -0
  573. package/package.json +2 -1
  574. package/resource/claude_gateway.jpeg +0 -0
  575. package/scripts/orchestration/bench-store.cjs +30 -0
  576. package/scripts/orchestration/inspect.cjs +16 -0
  577. package/scripts/orchestration/probe-profile.cjs +67 -0
  578. package/scripts/orchestration/reconcile.cjs +21 -0
  579. package/scripts/orchestration/smoke-elevenlabs.cjs +90 -0
  580. package/scripts/orchestration/smoke-media.cjs +70 -0
  581. package/scripts/orchestration/smoke-text.cjs +101 -0
  582. package/scripts/orchestration/smoke-vision.cjs +49 -0
@@ -35,6 +35,14 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.AgentRunner = exports.CHANNEL_COALESCE_WINDOW_MS = exports.WHATSAPP_CLOUD_WINDOW_NOTE = exports.DEFAULT_MODELS = exports.MAX_IMAGE_SIZE_BYTES = void 0;
37
37
  exports.toRelMediaFiles = toRelMediaFiles;
38
+ const channel_input_media_1 = require("../orchestration/channel-input-media");
39
+ const chunks_1 = require("../telegram/chunks");
40
+ const file_delivery_1 = require("../orchestration/file-delivery");
41
+ const voice_reply_policy_1 = require("../orchestration/voice-reply-policy");
42
+ const gateway_config_1 = require("../orchestration/gateway-config");
43
+ const delivery_1 = require("../orchestration/delivery");
44
+ const control_delivery_1 = require("../orchestration/control-delivery");
45
+ const stop_controls_1 = require("../orchestration/stop-controls");
38
46
  const crypto_1 = require("crypto");
39
47
  const events_1 = require("events");
40
48
  const fs = __importStar(require("fs"));
@@ -226,6 +234,229 @@ class AgentRunner extends events_1.EventEmitter {
226
234
  channelFor(chatId) {
227
235
  return this.channelSourceMap.get(chatId) ?? 'telegram';
228
236
  }
237
+ requiresDurableChannelIngress(source) {
238
+ return Boolean((this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(source)) ||
239
+ this.orchestration?.ownsChannelIngress(source));
240
+ }
241
+ orchestrationForApi(sessionId) {
242
+ return Boolean((this.orchestration?.ownsSession(sessionId) && !this.orchestration.canReturnToLegacy()) ||
243
+ (this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes('api')));
244
+ }
245
+ async sendLinkedOrchestrationChannel(...[binding, text, _id, file]) {
246
+ const channel = String(binding.channel), chat = String(binding.chat_id);
247
+ if (channel === 'wechat' && file)
248
+ return { state: 'failed', code: 'CHANNEL_ATTACHMENTS_UNSUPPORTED' };
249
+ let path;
250
+ if (file) {
251
+ try {
252
+ path = (0, file_delivery_1.resolveChannelFile)(this.agentConfig, file).path;
253
+ }
254
+ catch {
255
+ return { state: 'failed', code: 'ATTACHMENT_UNAVAILABLE' };
256
+ }
257
+ }
258
+ try {
259
+ if (channel === 'whatsapp') {
260
+ // Persist the receiving number in the binding; an in-memory last-chat map
261
+ // can change while a worker is running or disappear after a restart.
262
+ const thread = String(binding.thread_key ?? '');
263
+ const account = thread.startsWith('whatsapp-account:') ? thread.slice('whatsapp-account:'.length) : undefined;
264
+ await this.sendWhatsAppMessage(chat, file?.caption || text, path, account, { asDocument: !!file && file.kind !== 'image' });
265
+ }
266
+ else if (channel === 'wechat')
267
+ await this.sendWeChatMessage(chat, text);
268
+ else
269
+ return { state: 'failed', code: 'DELIVERY_NOT_CONFIGURED' };
270
+ return { state: 'delivered' };
271
+ }
272
+ catch {
273
+ return { state: 'unknown', code: 'PROVIDER_RECEIPT_UNKNOWN' };
274
+ }
275
+ }
276
+ async sendOrchestrationControl(channel, chatId, menu, meta) {
277
+ if (!['whatsapp', 'whatsapp_cloud', 'wechat'].includes(channel))
278
+ return (0, control_delivery_1.sendControlMenu)(this.agentConfig, channel, chatId, menu, meta);
279
+ const text = [menu.text, ...menu.buttons.map(button => `${button.label}: /orch ${button.data.replace(/^orch:/, '')}`)].join('\n');
280
+ const send = (0, delivery_1.channelSender)(() => this.agentConfig, fetch, () => false, (...args) => this.sendLinkedOrchestrationChannel(...args));
281
+ for (const part of (0, chunks_1.chunkText)(text, 1900)) {
282
+ const result = await send({ channel, chat_id: chatId, thread_key: channel === 'whatsapp' && meta.account_id ? `whatsapp-account:${meta.account_id}` : '' }, part, (0, crypto_1.randomUUID)());
283
+ if (result.state !== 'delivered')
284
+ throw new Error(result.code);
285
+ }
286
+ }
287
+ async getOrchestration() {
288
+ if (this.orchestration)
289
+ return this.orchestration;
290
+ if (!this.orchestrationStarting) {
291
+ this.orchestrationStarting = Promise.resolve().then(() => __importStar(require('../orchestration/runtime'))).then(({ AgentOrchestrationRuntime }) => AgentOrchestrationRuntime.open(this.agentConfig, this.gatewayConfig, this.agentDir, this.sessionStore, this.historyDb, {
292
+ sendLinkedChannel: (...args) => this.sendLinkedOrchestrationChannel(...args),
293
+ skills: () => this.skillRegistry,
294
+ refreshSkills: async () => {
295
+ try {
296
+ const { discoverCliSkills } = await Promise.resolve().then(() => __importStar(require('../orchestration/cli-skills')));
297
+ this.skillRegistry.cliSkills = await discoverCliSkills(this.agentConfig, undefined, this.gatewayConfig);
298
+ delete this.skillRegistry.cliDiscoveryError;
299
+ }
300
+ catch {
301
+ this.skillRegistry.cliSkills = [];
302
+ this.skillRegistry.cliDiscoveryError = 'CLI_SKILL_DISCOVERY_UNAVAILABLE';
303
+ }
304
+ },
305
+ onManagedTurn: (sessionId, text, metrics, skills) => {
306
+ const key = `orchestration:${sessionId}`;
307
+ this.skillLearning?.onTurnStart(key, sessionId, text, skills, metrics.startedAt);
308
+ for (const id of metrics.toolIds)
309
+ this.skillLearning?.onToolUse(key, id);
310
+ this.skillLearning?.onTokenUsage(key, metrics.inputTokens, metrics.totalTokens);
311
+ this.skillLearning?.onTurnEnd(key, sessionId);
312
+ },
313
+ createAgentSession: async (sessionId, profile, model, scope) => {
314
+ const key = scope && scope.source !== 'api' ? scope.chatId : sessionId;
315
+ const old = this.sessions.get(key);
316
+ if (old) {
317
+ if (!old.isIdle(0))
318
+ throw Object.assign(new Error('Agent session is busy'), { code: 'CONFLICT' });
319
+ await old.stop();
320
+ this.sessions.delete(key);
321
+ }
322
+ if (this.sessions.size >= this.maxConcurrent)
323
+ throw new Error('Session pool full');
324
+ if (this.agentConfig.type !== 'app-agent')
325
+ (0, knowledge_1.spawnArchiveReindex)(this.agentConfig.workspace, this.agentConfig.knowledge, this.gatewayConfig.gateway.knowledge);
326
+ const agentSession = new process_1.SessionProcess(sessionId, scope?.source ?? 'api', this.agentConfig, this.gatewayConfig, this.sessionStore, scope?.chatId, profile);
327
+ agentSession.historyLimit = (0, process_1.resolveMaxHistoryMessages)(this.agentConfig.history?.maxHistoryMessages, this.gatewayConfig.gateway.history?.maxHistoryMessages);
328
+ if (model)
329
+ agentSession.modelOverride = model;
330
+ this.sessions.set(key, agentSession);
331
+ return agentSession;
332
+ },
333
+ releaseAgentSession: async (sessionId, agentSession) => {
334
+ await agentSession.stop();
335
+ for (const [key, value] of this.sessions)
336
+ if (value === agentSession)
337
+ this.sessions.delete(key);
338
+ },
339
+ })).then(orchestration => { this.orchestration = orchestration; return orchestration; }).catch(error => { this.orchestrationStarting = undefined; throw error; });
340
+ }
341
+ return this.orchestrationStarting;
342
+ }
343
+ async orchestrationActivity(sessionId, principalId, after) {
344
+ return (await this.getOrchestration()).activity(sessionId, principalId, after);
345
+ }
346
+ async subscribeResponseText(sessionId, principalId, receive) {
347
+ return (await this.getOrchestration()).subscribeText(sessionId, principalId, receive);
348
+ }
349
+ async voiceReplay(sessionId, principalId, responseId) {
350
+ return (await this.getOrchestration()).voiceAudio(sessionId, principalId, responseId);
351
+ }
352
+ saveVoiceReplay(sessionId, principalId, responseId, audio) {
353
+ this.orchestration?.saveVoiceAudio(sessionId, principalId, responseId, audio);
354
+ }
355
+ async authorizeVoiceSession(sessionId, principalId) {
356
+ (await this.getOrchestration()).authorizeSession(sessionId, principalId);
357
+ }
358
+ async subscribeVoiceResults(sessionId, principalId, receive, gender) {
359
+ return (await this.getOrchestration()).subscribeVoiceResults(sessionId, principalId, receive, gender);
360
+ }
361
+ recordVoicePlayback(responseId, principalId, progress, state) {
362
+ this.orchestration?.recordPlayback(responseId, principalId, progress, state);
363
+ }
364
+ async submitVoiceUtterance(sessionId, chatId, principalId, text, utteranceId, allowTools, model) {
365
+ if (!this.agentConfig.orchestration?.enabled || !this.agentConfig.voice?.enabled)
366
+ throw new Error('Voice is disabled');
367
+ if (!(await this.apiSessionExists(chatId, sessionId)))
368
+ throw new Error('Session not found');
369
+ const orchestration = await this.getOrchestration();
370
+ const accepted = orchestration.submitInput({ scope: { agentId: this.agentConfig.id, agentSessionId: sessionId, source: 'api', accountId: principalId,
371
+ chatId, threadKey: '', principalId }, text, model, modality: 'live_voice', ingressKey: `utterance:${utteranceId}` }, { execute: allowTools, writeMemory: false });
372
+ // The browser refreshes history on utterance.accepted, before inference finishes.
373
+ await orchestration.flushHistory();
374
+ return { ...accepted, responseId: () => orchestration.responseIdForInput(accepted.inputId) };
375
+ }
376
+ async listApiTasks(sessionId, principalId, page = 0, pageSize = 10, includeFinished = false) {
377
+ if (!this.agentConfig.orchestration?.enabled)
378
+ throw new Error('ORCHESTRATION_DISABLED');
379
+ const runtime = await this.getOrchestration();
380
+ runtime.authorizeSession(sessionId, principalId);
381
+ return runtime.taskControls.list(sessionId, principalId, page, pageSize, includeFinished);
382
+ }
383
+ async showApiTask(sessionId, principalId, taskId) {
384
+ if (!this.agentConfig.orchestration?.enabled)
385
+ throw new Error('ORCHESTRATION_DISABLED');
386
+ const runtime = await this.getOrchestration();
387
+ const detail = runtime.taskControls.detail(sessionId, principalId, taskId);
388
+ const task = runtime.store.task(taskId);
389
+ // Full retained result, with worker-only skill context removed by status().
390
+ return { ...runtime.tasks.status(task.conversationId, principalId, taskId)[0], ...detail };
391
+ }
392
+ async cancelApiTask(sessionId, principalId, taskId) {
393
+ if (!this.agentConfig.orchestration?.enabled)
394
+ throw new Error('ORCHESTRATION_DISABLED');
395
+ const runtime = await this.getOrchestration();
396
+ const task = runtime.taskControls.cancel(sessionId, principalId, taskId);
397
+ return task;
398
+ }
399
+ stopVoiceResponse(sessionId) { this.orchestration?.stopResponse(sessionId); }
400
+ async sendOrchestratedApi(sessionId, chatId, message, opts, onText, onTool) {
401
+ const semantic = this.agentConfig.orchestration?.conversation?.semanticIntake === true;
402
+ if (!semantic && this.pendingApiSessions.has(sessionId))
403
+ throw Object.assign(new Error('Session already has a pending request'), { code: 'CONFLICT' });
404
+ if (!opts.principalId)
405
+ throw new Error('Authenticated principal required for conversation orchestration');
406
+ const deadline = Date.now() + opts.timeoutMs;
407
+ if (!semantic)
408
+ this.pendingApiSessions.add(sessionId); // reserve before the first await
409
+ try {
410
+ const orchestration = await this.getOrchestration();
411
+ await this.sessionStore.ensureApiSession(this.agentConfig.id, chatId, sessionId);
412
+ this.apiChatIds.set(sessionId, chatId);
413
+ const media = opts.mediaFiles?.length ? await promoteUiUploads(this.agentsBaseDir, this.agentConfig.id, sessionId, opts.mediaFiles, this.logger) : [];
414
+ const index = await this.sessionStore.loadIndex(this.agentConfig.id, chatId, 'api');
415
+ const saved = index?.sessions.find(session => session.id === sessionId)?.imageConfig;
416
+ const selected = { ...(saved ? AgentRunner.durableImageConfig(saved) : {}), ...opts.imageParams };
417
+ const imageParams = AgentRunner.remapImageParamsRefs(Object.keys(selected).length ? selected : undefined, opts.mediaFiles, media);
418
+ const videoParams = AgentRunner.remapVideoParamsRefs(opts.videoParams, opts.mediaFiles, media);
419
+ if (videoParams)
420
+ await this.sessionStore.updateSessionMeta(this.agentConfig.id, chatId, sessionId, { videoConfig: videoParams }, 'api');
421
+ const imageConfig = opts.imageParams && imageParams ? AgentRunner.durableImageConfig(imageParams) : undefined;
422
+ if (imageConfig)
423
+ await this.sessionStore.updateSessionMeta(this.agentConfig.id, chatId, sessionId, { imageConfig }, 'api');
424
+ const requestId = opts.requestId ?? (0, crypto_1.randomUUID)();
425
+ const scopedInput = { scope: { agentId: this.agentConfig.id, agentSessionId: sessionId, source: 'api', accountId: opts.principalId,
426
+ chatId, threadKey: '', principalId: opts.principalId }, text: message || '[Attachment inspection requested]', attachmentIds: media,
427
+ requestId, storeUserMessage: !opts.skipUserMessage,
428
+ metadata: imageParams || videoParams ? { promptContext: (imageParams ? AgentRunner.buildImageParamsNote(imageParams) : '') + (videoParams ? AgentRunner.buildVideoParamsNote(videoParams) : ''), imageRefs: imageParams?.image_refs } : undefined };
429
+ let text;
430
+ if (semantic) {
431
+ const accepted = orchestration.submitInput({ ...scopedInput, model: opts.model }, { execute: opts.allowTools ?? false, writeMemory: false }, onTool);
432
+ const lengths = new Map();
433
+ const unsubscribe = orchestration.subscribeText(sessionId, opts.principalId, event => {
434
+ if (!orchestration.responseBelongsToInput(accepted.inputId, event.responseId) || !event.text)
435
+ return;
436
+ const before = lengths.get(event.responseId) ?? 0;
437
+ if (event.text.length > before)
438
+ onText?.(event.text.slice(before));
439
+ lengths.set(event.responseId, event.text.length);
440
+ });
441
+ try {
442
+ text = await accepted.response;
443
+ }
444
+ finally {
445
+ unsubscribe();
446
+ }
447
+ }
448
+ else
449
+ text = await orchestration.send(scopedInput, { execute: opts.allowTools ?? false, writeMemory: false }, { timeoutMs: opts.timeoutMs, model: opts.model, onText, onTool });
450
+ if (opts.waitForTasks)
451
+ text = await orchestration.waitForTaskReport(sessionId, opts.principalId, requestId, text, deadline);
452
+ this.addApiAttachments(sessionId, orchestration.responseFiles(sessionId, requestId).map(ref => media_store_1.MediaStore.resolvePath(this.agentsBaseDir, this.agentConfig.id, ref)));
453
+ return { text, attachments: this.popApiAttachments(sessionId) };
454
+ }
455
+ finally {
456
+ if (!semantic)
457
+ this.pendingApiSessions.delete(sessionId);
458
+ }
459
+ }
229
460
  constructor(agentConfig, gatewayConfig, logger) {
230
461
  super();
231
462
  this.stopping = false;
@@ -246,6 +477,7 @@ class AgentRunner extends events_1.EventEmitter {
246
477
  this.slackThreadTs = new Map();
247
478
  this.receiver = null;
248
479
  this.discordReceiver = null;
480
+ this.discordCommandConfig = '';
249
481
  // LINE slow-LLM postback button manager (null when LINE disabled or threshold=0).
250
482
  this.lineReply = null;
251
483
  // Slack has no reply-token TTL to work around (see the plan's "no reply-manager
@@ -357,6 +589,7 @@ class AgentRunner extends events_1.EventEmitter {
357
589
  this.apiChatIds = new Map();
358
590
  // Skill registry for detecting /skill-name commands in user messages
359
591
  this.skillRegistry = { skills: new Map() };
592
+ this.skillNotificationOrigins = new Map();
360
593
  // Cancel function for the daily history cleanup timer
361
594
  this.cancelCleanup = null;
362
595
  /**
@@ -399,7 +632,8 @@ class AgentRunner extends events_1.EventEmitter {
399
632
  }
400
633
  });
401
634
  };
402
- this.agentConfig = agentConfig;
635
+ this.telegramCommandConfig = '';
636
+ this.agentConfig = (0, gateway_config_1.applyGatewayOrchestration)(agentConfig, gatewayConfig);
403
637
  this.gatewayConfig = gatewayConfig;
404
638
  this.logger = logger ?? (0, logger_1.createLogger)(agentConfig.id, gatewayConfig.gateway.logDir);
405
639
  // Resolve agentsBaseDir: workspace is at <agentsBaseDir>/<agentId>/workspace
@@ -428,6 +662,25 @@ class AgentRunner extends events_1.EventEmitter {
428
662
  return this.skillRegistry;
429
663
  }
430
664
  /** Wire the skill-learning manager (planning-62). Optional; unset = feature off. */
665
+ async notifySkillLearning(sessionId, text) {
666
+ if (this.orchestration?.ownsSession(sessionId)) {
667
+ const conversation = this.orchestration.store.get('SELECT source,chat_id FROM conversations WHERE agent_session_id=?', sessionId);
668
+ const source = conversation?.source;
669
+ const index = source && source !== 'api' ? await this.sessionStore.loadIndex(this.agentConfig.id, String(conversation.chat_id), source) : undefined;
670
+ await this.orchestration.notifySession(sessionId, text, source === 'api' || index?.activeSessionId === sessionId);
671
+ return;
672
+ }
673
+ const origin = this.skillNotificationOrigins.get(sessionId);
674
+ if (!origin)
675
+ return; // Diary only when the original destination is unavailable.
676
+ this.historyDb.insertMessage({ chatId: `${origin.source}-${origin.chatId}`, sessionId, source: origin.source, role: 'assistant', content: text, ts: Date.now() });
677
+ const index = await this.sessionStore.loadIndex(this.agentConfig.id, origin.chatId, origin.source);
678
+ if (index?.activeSessionId !== sessionId)
679
+ return;
680
+ const result = await (0, delivery_1.channelSender)(() => this.agentConfig)({ channel: origin.source, chat_id: origin.chatId, thread_key: origin.thread }, text, (0, crypto_1.randomUUID)());
681
+ if (result.state !== 'delivered')
682
+ this.logger.warn('Skill notification delivery failed', { sessionId, code: result.code });
683
+ }
431
684
  setSkillLearning(manager) {
432
685
  this.skillLearning = manager;
433
686
  }
@@ -453,7 +706,7 @@ class AgentRunner extends events_1.EventEmitter {
453
706
  req.on('data', (chunk) => {
454
707
  raw += chunk;
455
708
  });
456
- req.on('end', () => {
709
+ req.on('end', async () => {
457
710
  if (url.pathname === '/command') {
458
711
  this.handleCommandRequest(raw, res);
459
712
  return;
@@ -471,8 +724,6 @@ class AgentRunner extends events_1.EventEmitter {
471
724
  // receiver (Bun) can try to reuse the stale socket → "socket connection
472
725
  // was closed unexpectedly". Closing per-request avoids the race entirely.
473
726
  res.setHeader('Connection', 'close');
474
- res.writeHead(200);
475
- res.end('ok');
476
727
  try {
477
728
  const params = JSON.parse(raw);
478
729
  const meta = params.meta ?? {};
@@ -486,6 +737,66 @@ class AgentRunner extends events_1.EventEmitter {
486
737
  // forget adding to a chain of `=== 'x' ? 'x' : ...` checks.
487
738
  const channelSource = ((0, types_1.isChatChannel)(meta['source']) ? meta['source'] : 'telegram');
488
739
  this.channelSourceMap.set(chatId, channelSource);
740
+ const channelOrchestration = this.orchestration?.ownsChannel(channelSource, chatId) || (this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(channelSource));
741
+ if (channelSource !== 'telegram' && this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(channelSource) && /^\/sessions?(?:\s|$)/.test(content.trim())) {
742
+ const index = await this.sessionStore.listSessions(this.agentConfig.id, chatId, channelSource);
743
+ const current = index.sessions.find(session => session.id === index.activeSessionId);
744
+ const text = content.trim() === '/sessions'
745
+ ? `Sessions\n${index.sessions.slice(0, 15).map(session => `${session.id === index.activeSessionId ? '✅ ' : ''}${session.name}\n${session.id}`).join('\n\n')}`
746
+ : `Current session: ${current?.name ?? '(unnamed)'}\n${index.activeSessionId}\nMode: Orchestration\nModel: ${this.agentConfig.claude.model}\nMessages: ${current?.messageCount ?? 0}\n\nCommands: /session /sessions /voice /voices /tasks /stop`;
747
+ await this.sendOrchestrationControl(channelSource, chatId, { text, buttons: [] }, meta);
748
+ res.writeHead(200);
749
+ res.end('ok');
750
+ return;
751
+ }
752
+ if (channelSource !== 'telegram' && (/^\/(voice|voices|tasks|stop|orch)(?:\s|$)/.test(content.trim()) || (this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(channelSource) && content.trim() === '/help'))) {
753
+ const enabled = !!(this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(channelSource));
754
+ if (enabled) {
755
+ const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, channelSource);
756
+ const runtime = await this.getOrchestration();
757
+ const scope = { channel: channelSource, chatId, thread: meta.thread_ts ?? meta.message_thread_id ?? '', sessionId, principalId: `${channelSource}:${meta.user_id ?? meta.user ?? chatId}` };
758
+ let menu;
759
+ try {
760
+ menu = await runtime.channelControls.handle(scope, content.trim());
761
+ }
762
+ catch {
763
+ menu = { text: 'Control unavailable or expired. Please open the command again.', buttons: [] };
764
+ }
765
+ await this.sendOrchestrationControl(channelSource, chatId, menu, meta);
766
+ res.writeHead(200);
767
+ res.end('ok');
768
+ return;
769
+ }
770
+ // Keep the legacy /stop path, but never turn disabled controls into model prompts.
771
+ if (!/^\/stop(?:\s|$)/.test(content.trim())) {
772
+ await this.sendOrchestrationControl(channelSource, chatId, { text: 'These controls require orchestration mode.', buttons: [] }, meta);
773
+ res.writeHead(200);
774
+ res.end('ok');
775
+ return;
776
+ }
777
+ }
778
+ if (channelOrchestration && !(0, builtin_commands_1.isBuiltinCommand)(content.trim(), channelSource)) {
779
+ const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, channelSource);
780
+ const orchestration = await this.getOrchestration();
781
+ const { media, quoted, details } = await (0, channel_input_media_1.channelInputMedia)(this.agentConfig, this.agentsBaseDir, channelSource, chatId, meta, AgentRunner.discardEphemeralStaging);
782
+ const senderId = meta.user_id ?? meta.user ?? chatId;
783
+ const accepted = orchestration.submitInput({ scope: { agentId: this.agentConfig.id, agentSessionId: sessionId, source: channelSource,
784
+ accountId: this.agentConfig.id, chatId, threadKey: channelSource === 'whatsapp' && meta.account_id ? `whatsapp-account:${meta.account_id}` : meta.thread_ts ?? meta.message_thread_id ?? '', principalId: `${channelSource}:${senderId}` },
785
+ text: content || '[Attachment inspection requested]', attachmentIds: media, trustedChannelMember: true,
786
+ modality: ['voice', 'audio'].includes(meta.media_type ?? meta.attachment_kind) ? 'voice_note' : 'text',
787
+ ingressKey: meta.message_id, metadata: { senderId, senderName: meta.sender_name, platformMessageId: meta.message_id, platformMessageIds: meta.message_ids_json ? JSON.parse(meta.message_ids_json) : undefined, mediaGroupId: meta.media_group_id,
788
+ attachmentName: meta.attachment_name, mediaType: meta.media_type ?? meta.attachment_kind, repliedText: meta.replied_text, repliedMessageId: meta.replied_message_id, repliedSender: meta.replied_user, repliedAttachmentIds: quoted, attachmentDetails: details, attachmentError: meta.attachment_error } }, { execute: this.agentConfig.allow_tools !== false, writeMemory: this.agentConfig.allow_tools !== false });
789
+ // The durable receipt precedes receiver acknowledgment and inference.
790
+ res.writeHead(200);
791
+ res.end('ok');
792
+ void accepted.response.catch(error => {
793
+ // The durable decision/outbox owns failure delivery and activity, including autonomous turns.
794
+ this.logger.error('Conversation channel turn failed', { chatId, code: error.code ?? 'ORCHESTRATION_ERROR', ...(error.timeout ? { timeout: error.timeout } : {}) });
795
+ });
796
+ return;
797
+ }
798
+ res.writeHead(200);
799
+ res.end('ok');
489
800
  // Slack: remember the current message's thread context so the
490
801
  // auto-forward fallback (writeAutoForward) can stay in-thread. A
491
802
  // top-level message clears it so a later fallback isn't misrouted
@@ -554,6 +865,10 @@ class AgentRunner extends events_1.EventEmitter {
554
865
  return;
555
866
  }
556
867
  catch (err) {
868
+ if (!res.headersSent) {
869
+ res.writeHead(503);
870
+ res.end('Channel input was not accepted');
871
+ }
557
872
  this.logger.warn('Failed to parse channel callback body', {
558
873
  error: err.message,
559
874
  });
@@ -597,6 +912,10 @@ class AgentRunner extends events_1.EventEmitter {
597
912
  return;
598
913
  }
599
914
  const session = this.sessions.get(chatId);
915
+ if (session?.runtimeProfile || this.requiresDurableChannelIngress('telegram')) {
916
+ respond({ ok: false, error: 'Recovery is owned by orchestration; legacy turn replay is disabled.' });
917
+ return;
918
+ }
600
919
  const autoRecover = this.gatewayConfig.gateway.selfHealing?.autoRecover === true;
601
920
  const req = {
602
921
  incidentId: body.incidentId ?? '',
@@ -764,6 +1083,119 @@ class AgentRunner extends events_1.EventEmitter {
764
1083
  return;
765
1084
  }
766
1085
  const command = body.command;
1086
+ if (command === 'telegram_voices') {
1087
+ const chatId = body.chat_id ?? '', payload = body.payload ?? {};
1088
+ if (!/^\d+$/.test(chatId) || payload.user_id !== chatId) {
1089
+ respond({ success: false }, 400);
1090
+ return;
1091
+ }
1092
+ if (!this.agentConfig.orchestration?.enabled || !this.agentConfig.orchestration.channels?.includes('telegram')) {
1093
+ respond({ success: false, error: 'Voice controls unavailable.' });
1094
+ return;
1095
+ }
1096
+ try {
1097
+ const voices = (await this.getOrchestration()).telegramVoices;
1098
+ if (payload.action === 'dismiss' && typeof payload.menu_id === 'string') {
1099
+ voices.dismiss(chatId, payload.menu_id);
1100
+ respond({ success: true });
1101
+ }
1102
+ else if (payload.action === 'choose' && typeof payload.menu_id === 'string' && typeof payload.index === 'number')
1103
+ respond({ success: true, ...await voices.choose(chatId, payload.menu_id, payload.index) });
1104
+ else
1105
+ respond({ success: true, ...await voices.menu(chatId, typeof payload.page === 'number' ? payload.page : 0, typeof payload.menu_id === 'string' ? payload.menu_id : undefined, typeof payload.gender === 'string' ? payload.gender : undefined) });
1106
+ }
1107
+ catch {
1108
+ respond({ success: false, error: 'Voice list unavailable or provider changed. Use /voices to refresh.' });
1109
+ }
1110
+ return;
1111
+ }
1112
+ if (command === 'telegram_tasks') {
1113
+ const chatId = body.chat_id ?? '', payload = body.payload ?? {}, principalId = `telegram:${payload.user_id ?? ''}`;
1114
+ if (!/^\d+$/.test(chatId) || payload.user_id !== chatId) {
1115
+ respond({ success: false }, 400);
1116
+ return;
1117
+ }
1118
+ if (!this.agentConfig.orchestration?.enabled || !this.agentConfig.orchestration.channels?.includes('telegram')) {
1119
+ respond({ success: false, error: 'Task controls require orchestration mode.' });
1120
+ return;
1121
+ }
1122
+ try {
1123
+ const runtime = await this.getOrchestration();
1124
+ const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, 'telegram');
1125
+ if (payload.session_id !== undefined && payload.session_id !== sessionId) {
1126
+ respond({ success: false, error: 'TASK_SESSION_CHANGED' });
1127
+ return;
1128
+ }
1129
+ if (payload.action === 'detail' || payload.action === 'cancel') {
1130
+ if (typeof payload.task_id !== 'string')
1131
+ throw Error('INVALID_TASK');
1132
+ const task = payload.action === 'cancel' ? runtime.taskControls.cancel(sessionId, principalId, payload.task_id) : runtime.taskControls.detail(sessionId, principalId, payload.task_id);
1133
+ respond({ success: true, sessionId, task });
1134
+ }
1135
+ else
1136
+ respond({ success: true, sessionId, ...runtime.taskControls.list(sessionId, principalId, typeof payload.page === 'number' ? payload.page : 0) });
1137
+ }
1138
+ catch {
1139
+ respond({ success: false, error: 'Task unavailable. Use /tasks to refresh.' });
1140
+ }
1141
+ return;
1142
+ }
1143
+ if (command === 'task_stop') {
1144
+ const chatId = body.chat_id ?? '', principalId = `telegram:${body.payload?.user_id ?? ''}`;
1145
+ if (!/^\d+$/.test(chatId) || body.payload?.user_id !== chatId) {
1146
+ respond({ success: false, error: 'invalid_request' }, 400);
1147
+ return;
1148
+ }
1149
+ try {
1150
+ if (!(this.agentConfig.orchestration?.enabled && this.agentConfig.orchestration.channels?.includes('telegram'))) {
1151
+ if (body.payload?.menu_id) {
1152
+ respond({ success: false, error: 'Orchestration is disabled.' });
1153
+ return;
1154
+ }
1155
+ await this.handleCommandStop(chatId);
1156
+ respond({ success: true, legacy: true });
1157
+ return;
1158
+ }
1159
+ const runtime = await this.getOrchestration();
1160
+ const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, 'telegram');
1161
+ if (typeof body.payload?.menu_id === 'string' && typeof body.payload?.index === 'number') {
1162
+ respond({ success: true, ...runtime.stopControls.choose(sessionId, principalId, body.payload.menu_id, body.payload.index) });
1163
+ }
1164
+ else {
1165
+ const menu = runtime.stopControls.open(sessionId, principalId);
1166
+ respond({ success: true, ...menu, text: (0, stop_controls_1.stopMenuText)(menu, true) });
1167
+ }
1168
+ }
1169
+ catch {
1170
+ respond({ success: false, error: 'Selection unavailable. Use /stop to refresh the task list.' });
1171
+ }
1172
+ return;
1173
+ }
1174
+ if (command === 'telegram_voice') {
1175
+ const chatId = body.chat_id ?? '';
1176
+ const value = body.payload?.enabled;
1177
+ const mode = body.payload?.mode;
1178
+ if (!/^\d+$/.test(chatId) || (value !== undefined && typeof value !== 'boolean') || (mode !== undefined && !voice_reply_policy_1.VOICE_REPLY_MODES.includes(mode))) {
1179
+ respond({ success: false, error: 'invalid_request' }, 400);
1180
+ return;
1181
+ }
1182
+ if (!this.agentConfig.orchestration?.enabled || !this.agentConfig.orchestration.channels?.includes('telegram') || !this.agentConfig.voice?.enabled || this.agentConfig.voice?.notes?.replyWithVoice === false) {
1183
+ respond({ success: false, error: 'voice_not_configured' });
1184
+ return;
1185
+ }
1186
+ try {
1187
+ const runtime = await this.getOrchestration();
1188
+ if (mode !== undefined)
1189
+ runtime.store.setTelegramVoiceMode(chatId, mode);
1190
+ else if (typeof value === 'boolean')
1191
+ runtime.store.setTelegramVoice(chatId, value);
1192
+ respond({ success: true, enabled: runtime.store.telegramVoice(chatId), mode: runtime.store.telegramVoiceMode(chatId) });
1193
+ }
1194
+ catch {
1195
+ respond({ success: false, error: 'voice_settings_unavailable' }, 500);
1196
+ }
1197
+ return;
1198
+ }
767
1199
  if (command === 'get_model') {
768
1200
  respond({ model: this.agentConfig.claude.model });
769
1201
  return;
@@ -796,6 +1228,10 @@ class AgentRunner extends events_1.EventEmitter {
796
1228
  // its initData. The browser-facing routes live on the gateway; here we only
797
1229
  // mint the pairing and hand back the phone-openable link.
798
1230
  if (command === 'cli_pair') {
1231
+ if (this.gatewayConfig.gateway.headless !== false || this.agentConfig.type === 'app-agent' || this.agentConfig.orchestration?.enabled) {
1232
+ respond({ success: false, error: 'interactive_mode_required' });
1233
+ return;
1234
+ }
799
1235
  const payload = body.payload ?? {};
800
1236
  const channel = payload['channel'];
801
1237
  const userId = typeof payload['user_id'] === 'string' ? payload['user_id'] : '';
@@ -1240,6 +1676,8 @@ class AgentRunner extends events_1.EventEmitter {
1240
1676
  session.setProcessing(true);
1241
1677
  const turnText = blocks.join('\n');
1242
1678
  // Skill-learning: mark the start of a user turn (cheap, best-effort).
1679
+ const originMeta = entries[0]?.meta ?? {};
1680
+ this.skillNotificationOrigins.set(sessionId, { source: channelSource, chatId, thread: originMeta.thread_ts ?? originMeta.message_thread_id ?? '' });
1243
1681
  this.skillLearning?.onTurnStart(chatId, sessionId, entries[0]?.content ?? '', invokedSkills);
1244
1682
  session.sendMessage(turnText);
1245
1683
  // Remember this turn for the C1 guarded resend (Phase 3b): reset the
@@ -2115,7 +2553,7 @@ class AgentRunner extends events_1.EventEmitter {
2115
2553
  // event. Start the typing-done timer here so the indicator stays alive
2116
2554
  // through multi-turn tool-call sequences and only stops when all work is done.
2117
2555
  if (obj['type'] === 'session_idle' && proc.backend === 'pty-shell') {
2118
- // A parent can become idle immediately after dispatching background
2556
+ // An agent can become idle immediately after dispatching background
2119
2557
  // Agent/Workflow work. Keep the Telegram typing signal and processing
2120
2558
  // sentinel alive until the task notification starts its follow-up turn.
2121
2559
  const waitingForBackgroundWork = proc.retainBackgroundWorkingState();
@@ -2171,7 +2609,7 @@ class AgentRunner extends events_1.EventEmitter {
2171
2609
  }, this.channelFor(mapKey)).catch(() => { });
2172
2610
  }
2173
2611
  proc.on('backgroundWorkExpired', () => {
2174
- // The child task never notified its parent within the bounded grace
2612
+ // The child task never notified its agent within the bounded grace
2175
2613
  // window; release the Telegram typing signal instead of leaving it live.
2176
2614
  this.writeTypingDone(mapKey);
2177
2615
  });
@@ -2236,6 +2674,9 @@ class AgentRunner extends events_1.EventEmitter {
2236
2674
  });
2237
2675
  return proc;
2238
2676
  }
2677
+ apiTaskStopReply(sessionId, principalId, text) {
2678
+ return this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes('api') ? this.orchestration?.stopControls.replyCommand(sessionId, principalId, text) : undefined;
2679
+ }
2239
2680
  static isApiBuiltinCommand(content) {
2240
2681
  return (0, builtin_commands_1.isBuiltinCommand)(content, 'api');
2241
2682
  }
@@ -2435,6 +2876,13 @@ class AgentRunner extends events_1.EventEmitter {
2435
2876
  * "No turn in progress." that fired whenever interrupt() no-op'd mid-spawn.
2436
2877
  */
2437
2878
  async handleCommandStop(chatId) {
2879
+ if (this.orchestration && this.agentConfig.orchestration?.enabled) {
2880
+ const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, this.channelFor(chatId));
2881
+ if (this.orchestration.stopResponse(sessionId)) {
2882
+ this.writeAutoForward(chatId, 'Response stopped. Tasks remain active.');
2883
+ return;
2884
+ }
2885
+ }
2438
2886
  // Cancel queued + buffered work first so nothing flushes in behind the interrupt.
2439
2887
  const buffered = this.channelCoalesce.get(chatId);
2440
2888
  if (buffered?.timer)
@@ -2816,7 +3264,9 @@ class AgentRunner extends events_1.EventEmitter {
2816
3264
  const { immediate, deferred } = await this.restartOrDefer({
2817
3265
  skipBusy: false,
2818
3266
  deferIdle: true,
2819
- filter: (proc) => proc.connectorConfigChanged(connectorId, target),
3267
+ // Managed turns use a fresh subprocess/config on their next decision or task.
3268
+ // Never send them through legacy restart/replay while effects may be in flight.
3269
+ filter: (proc) => !proc.runtimeProfile && proc.connectorConfigChanged(connectorId, target),
2820
3270
  });
2821
3271
  return { restarted: immediate + deferred > 0 };
2822
3272
  }
@@ -3043,7 +3493,7 @@ class AgentRunner extends events_1.EventEmitter {
3043
3493
  startIdleCleaner() {
3044
3494
  this.idleCleanerTimer = setInterval(async () => {
3045
3495
  for (const [id, proc] of this.sessions) {
3046
- // A parent that just dispatched Agent/Workflow/Monitor work is CLI-idle
3496
+ // An agent that just dispatched Agent/Workflow/Monitor work is CLI-idle
3047
3497
  // but that dispatch may still need this session to receive its
3048
3498
  // eventual task notification.
3049
3499
  if (proc.hasLikelyOutstandingBackgroundWork())
@@ -3058,16 +3508,20 @@ class AgentRunner extends events_1.EventEmitter {
3058
3508
  }, 5 * 60 * 1000);
3059
3509
  }
3060
3510
  async start() {
3511
+ if (this.agentConfig.orchestration?.enabled || fs.existsSync(path.join(this.agentDir, 'orchestration.db')))
3512
+ await this.getOrchestration();
3061
3513
  this.stopping = false;
3062
3514
  await this.sweepStaleSessionDirs();
3063
3515
  await this.startCallbackServer();
3064
3516
  if (this.agentConfig.telegram?.botToken) {
3065
- this.receiver = new receiver_1.TelegramReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir);
3517
+ this.receiver = new receiver_1.TelegramReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir, this.gatewayConfig.gateway.headless !== false);
3066
3518
  this.receiver.start();
3519
+ this.telegramCommandConfig = this.telegramCommandsKey();
3067
3520
  }
3068
3521
  if (this.agentConfig.discord?.botToken) {
3069
- this.discordReceiver = new receiver_2.DiscordReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir);
3522
+ this.discordReceiver = new receiver_2.DiscordReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir, this.gatewayConfig.gateway.headless !== false);
3070
3523
  this.discordReceiver.start();
3524
+ this.discordCommandConfig = this.telegramCommandsKey();
3071
3525
  }
3072
3526
  // LINE slow-LLM postback button: gateway-side token lifecycle + cache.
3073
3527
  // Enabled for any LINE agent unless its threshold is 0 (then the MCP
@@ -3128,8 +3582,34 @@ class AgentRunner extends events_1.EventEmitter {
3128
3582
  }
3129
3583
  }
3130
3584
  }
3585
+ telegramCommandsKey() {
3586
+ return JSON.stringify([this.gatewayConfig.gateway.headless !== false, this.agentConfig.type,
3587
+ Boolean(this.agentConfig.orchestration?.enabled), this.agentConfig.orchestration?.channels ?? ['api']]);
3588
+ }
3589
+ /** Reload only the receiver when command visibility changes; Agent/Worker work continues. */
3590
+ refreshTelegramCommands() {
3591
+ const discord = this.discordReceiver, discordKey = this.telegramCommandsKey();
3592
+ if (discord && discordKey !== this.discordCommandConfig) {
3593
+ this.discordCommandConfig = discordKey;
3594
+ void discord.stop().then(() => { if (this.discordReceiver !== discord || this.stopping)
3595
+ return; this.discordReceiver = null; this.startDiscordReceiver(); });
3596
+ }
3597
+ const previous = this.receiver, key = this.telegramCommandsKey();
3598
+ if (!previous || key === this.telegramCommandConfig)
3599
+ return;
3600
+ this.telegramCommandConfig = key;
3601
+ void previous.stop().then(() => {
3602
+ if (this.receiver !== previous || this.stopping)
3603
+ return;
3604
+ this.receiver = null;
3605
+ this.startTelegramReceiver();
3606
+ });
3607
+ }
3131
3608
  updateAgentConfig(newConfig) {
3609
+ newConfig = (0, gateway_config_1.applyGatewayOrchestration)(newConfig, this.gatewayConfig);
3610
+ this.orchestration?.updateAgentConfig(newConfig);
3132
3611
  this.agentConfig = newConfig;
3612
+ this.refreshTelegramCommands();
3133
3613
  // Restart LineReplyManager if the LINE config changed so the live instance
3134
3614
  // picks up a new access token, threshold, or labels without a full restart.
3135
3615
  this.stopLineReply();
@@ -3381,6 +3861,25 @@ class AgentRunner extends events_1.EventEmitter {
3381
3861
  });
3382
3862
  }
3383
3863
  }
3864
+ if (update.reply) {
3865
+ if (update.reply.messageId)
3866
+ meta.replied_message_id = update.reply.messageId;
3867
+ if (update.reply.text)
3868
+ meta.replied_text = update.reply.text;
3869
+ const ref = update.reply.image ?? update.reply.file;
3870
+ if (ref)
3871
+ try {
3872
+ const bytes = await (0, ilink_client_1.downloadWeixinImage)(ref, { maxBytes: media_store_1.MediaStore.maxUploadBytes });
3873
+ const name = update.reply.file?.fileName.replace(/[^a-zA-Z0-9_.-]/g, '_') ?? 'image.jpg';
3874
+ const dest = path.join(os.tmpdir(), `wechat-reply-${safeId}-${Date.now()}-${name}`);
3875
+ await fsPromises.writeFile(dest, bytes, { mode: 0o600 });
3876
+ meta.replied_image_path = dest;
3877
+ meta.media_ephemeral = '1';
3878
+ }
3879
+ catch {
3880
+ meta.attachment_error = 'The quoted attachment could not be downloaded. Do not claim to have read it.';
3881
+ }
3882
+ }
3384
3883
  try {
3385
3884
  await fetch(`http://127.0.0.1:${this.callbackPort}/channel`, {
3386
3885
  method: 'POST',
@@ -3458,8 +3957,9 @@ class AgentRunner extends events_1.EventEmitter {
3458
3957
  return;
3459
3958
  if (!this.agentConfig.telegram?.botToken)
3460
3959
  return;
3461
- this.receiver = new receiver_1.TelegramReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir);
3960
+ this.receiver = new receiver_1.TelegramReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir, this.gatewayConfig.gateway.headless !== false);
3462
3961
  this.receiver.start();
3962
+ this.telegramCommandConfig = this.telegramCommandsKey();
3463
3963
  this.logger.info('TelegramReceiver hot-started', { agentId: this.agentConfig.id });
3464
3964
  }
3465
3965
  stopTelegramReceiver() {
@@ -3477,8 +3977,9 @@ class AgentRunner extends events_1.EventEmitter {
3477
3977
  return;
3478
3978
  if (!this.agentConfig.discord?.botToken)
3479
3979
  return;
3480
- this.discordReceiver = new receiver_2.DiscordReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir);
3980
+ this.discordReceiver = new receiver_2.DiscordReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir, this.gatewayConfig.gateway.headless !== false);
3481
3981
  this.discordReceiver.start();
3982
+ this.discordCommandConfig = this.telegramCommandsKey();
3482
3983
  this.logger.info('DiscordReceiver hot-started', { agentId: this.agentConfig.id });
3483
3984
  }
3484
3985
  stopDiscordReceiver() {
@@ -3493,6 +3994,12 @@ class AgentRunner extends events_1.EventEmitter {
3493
3994
  }
3494
3995
  async stop() {
3495
3996
  this.stopping = true;
3997
+ if (this.orchestrationStarting) {
3998
+ const orchestration = await this.orchestrationStarting.catch(() => undefined);
3999
+ await orchestration?.close();
4000
+ this.orchestration = undefined;
4001
+ this.orchestrationStarting = undefined;
4002
+ }
3496
4003
  if (this.idleCleanerTimer !== null) {
3497
4004
  clearInterval(this.idleCleanerTimer);
3498
4005
  this.idleCleanerTimer = null;
@@ -3575,6 +4082,17 @@ class AgentRunner extends events_1.EventEmitter {
3575
4082
  isRunning() {
3576
4083
  return this.receiver?.isRunning() ?? false;
3577
4084
  }
4085
+ getOrchestrationSummary() {
4086
+ if (!this.agentConfig.orchestration?.enabled && !this.orchestration)
4087
+ return undefined;
4088
+ const summary = this.orchestration?.dashboardSummary() ?? { enabled: true, backend: 'headless', workspaceMode: this.agentConfig.type === 'app-agent' ? 'container' : this.agentConfig.orchestration?.tasks?.workspaceMode ?? 'host', activeAgentSessions: [], tasks: [], sessions: [] };
4089
+ const processes = [...this.sessions.values()].filter(p => p.runtimeProfile?.role === 'agent' && p.isRunning());
4090
+ return { ...summary, sessions: summary.sessions.map(s => {
4091
+ const p = processes.find(p => p.sessionId === s.sessionId);
4092
+ return { ...s, model: p?.model ?? s.model, tokens: p?.totalTokens ?? 0, spawnedAt: p?.spawnedAt ?? 0,
4093
+ uptimeSec: p ? Math.floor((Date.now() - p.spawnedAt) / 1000) : 0 };
4094
+ }), agentProcesses: processes.map(p => ({ sessionId: p.sessionId, hostProcessId: p.processId, container: this.agentConfig.container })) };
4095
+ }
3578
4096
  getSessionsSummary() {
3579
4097
  const now = Date.now();
3580
4098
  // A single logical session can appear multiple times in the ring buffer: a
@@ -3612,6 +4130,8 @@ class AgentRunner extends events_1.EventEmitter {
3612
4130
  * - Appends user message and assistant reply to SessionStore for history persistence.
3613
4131
  */
3614
4132
  async sendApiMessage(sessionId, chatId, message, opts) {
4133
+ if (this.orchestrationForApi(sessionId))
4134
+ return this.sendOrchestratedApi(sessionId, chatId, message, opts);
3615
4135
  if (this.pendingApiSessions.has(sessionId)) {
3616
4136
  const err = Object.assign(new Error(`Session ${sessionId} already has a pending request`), { code: 'CONFLICT' });
3617
4137
  throw err;
@@ -3894,6 +4414,18 @@ class AgentRunner extends events_1.EventEmitter {
3894
4414
  * expires (#421).
3895
4415
  */
3896
4416
  async sendApiMessageStream(sessionId, chatId, message, callbacks, opts) {
4417
+ if (this.orchestrationForApi(sessionId)) {
4418
+ if (this.pendingApiSessions.has(sessionId))
4419
+ throw Object.assign(new Error('Session already has a pending request'), { code: 'CONFLICT' });
4420
+ const turn = this.turnStreams.start((0, turn_stream_1.turnStreamKey)('api', sessionId), opts.requestId ?? (0, crypto_1.randomUUID)());
4421
+ const sink = (0, turn_stream_1.callbackSink)(callbacks);
4422
+ turn.attach(sink, 0);
4423
+ void this.sendOrchestratedApi(sessionId, chatId, message, { ...opts, requestId: turn.requestId }, text => turn.emit({ type: 'text_delta', text }), event => { if (event.type === 'tool_use')
4424
+ turn.emit({ ...event, type: 'tool_use' }); })
4425
+ .then(result => this.turnStreams.complete(turn, (0, turn_stream_1.resultEvent)(result.text, result.attachments)))
4426
+ .catch(error => this.turnStreams.complete(turn, (0, turn_stream_1.errorEvent)(error), error));
4427
+ return () => turn.detach(sink);
4428
+ }
3897
4429
  if (this.pendingApiSessions.has(sessionId)) {
3898
4430
  const err = Object.assign(new Error(`Session ${sessionId} already has a pending request`), { code: 'CONFLICT' });
3899
4431
  throw err;
@@ -4365,6 +4897,8 @@ class AgentRunner extends events_1.EventEmitter {
4365
4897
  if (!AgentRunner.isApiBuiltinCommand(cmd)) {
4366
4898
  throw new Error(`Unknown command: ${cmd}`);
4367
4899
  }
4900
+ if (cmd === '/stop' && this.agentConfig.orchestration?.enabled && opts?.principalId)
4901
+ this.orchestration?.authorizeSession(sessionId, opts.principalId);
4368
4902
  // Register session in the api-{chatId} index on first use (same as sendApiMessageStream)
4369
4903
  await this.sessionStore.ensureApiSession(agentId, storeChatId, sessionId).catch((err) => {
4370
4904
  this.logger.warn('Failed to register API session in index for command', { agentId, chatId, sessionId, error: err.message });
@@ -4387,7 +4921,7 @@ class AgentRunner extends events_1.EventEmitter {
4387
4921
  // Persist the full user command before executing so it appears in history.
4388
4922
  // Skip for /clear — clearSession() below wipes the table anyway; only the response survives.
4389
4923
  if (cmd !== '/clear') {
4390
- persist('user', command);
4924
+ persist('user', opts?.displayCommand ?? command);
4391
4925
  }
4392
4926
  let result;
4393
4927
  let responseText;
@@ -4402,17 +4936,41 @@ class AgentRunner extends events_1.EventEmitter {
4402
4936
  : `Current model: ${model}`;
4403
4937
  }
4404
4938
  else if (cmd === '/stop') {
4405
- const session = this.sessions.get(sessionId);
4406
- const stopped = session ? session.interrupt() : false;
4407
- result = { stopped };
4408
- responseText = stopped
4409
- ? 'Session was interrupted before I could respond.'
4410
- : 'No active session to stop.';
4411
- // A turn was actually cut off — always leave a visible note in history so the
4412
- // web Stop button (skipPersist:true, to suppress the "/stop" command echo) and
4413
- // a typed /stop command show the same outcome instead of the button leaving a
4414
- // silently-dangling user turn.
4415
- forcePersist = stopped;
4939
+ if (this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes('api') && this.orchestration && opts?.principalId) {
4940
+ const selection = /^\/stop\s+([a-f0-9-]{36})\s+(\d+)$/.exec(command.trim());
4941
+ if (selection) {
4942
+ try {
4943
+ const selected = this.orchestration.stopControls.choose(sessionId, opts.principalId, selection[1], Number(selection[2]));
4944
+ result = { ...selected };
4945
+ responseText = selected.text;
4946
+ }
4947
+ catch {
4948
+ result = { error: 'STOP_SELECTION_UNAVAILABLE' };
4949
+ responseText = 'Selection unavailable. Use /stop to refresh the task list.';
4950
+ }
4951
+ }
4952
+ else {
4953
+ const menu = this.orchestration.stopControls.open(sessionId, opts.principalId);
4954
+ responseText = (0, stop_controls_1.stopMenuText)(menu);
4955
+ result = { ...menu, responseText };
4956
+ }
4957
+ forcePersist = true;
4958
+ }
4959
+ else {
4960
+ const session = this.sessions.get(sessionId);
4961
+ const stopped = this.agentConfig.orchestration?.enabled && this.orchestration?.isBusy(sessionId)
4962
+ ? this.orchestration.stopResponse(sessionId)
4963
+ : session ? session.interrupt() : false;
4964
+ result = { stopped };
4965
+ responseText = stopped
4966
+ ? 'Session was interrupted before I could respond.'
4967
+ : 'No active session to stop.';
4968
+ // A turn was actually cut off — always leave a visible note in history so the
4969
+ // web Stop button (skipPersist:true, to suppress the "/stop" command echo) and
4970
+ // a typed /stop command show the same outcome instead of the button leaving a
4971
+ // silently-dangling user turn.
4972
+ forcePersist = stopped;
4973
+ }
4416
4974
  }
4417
4975
  else if (cmd === '/restart') {
4418
4976
  this.restartProcess(sessionId).catch(() => { });
@@ -4692,6 +5250,100 @@ class AgentRunner extends events_1.EventEmitter {
4692
5250
  const mediaPaths = this.historyDb.clearSession(`api-${chatId}`, sessionId);
4693
5251
  media_store_1.MediaStore.deleteMediaFiles(this.agentsBaseDir, this.agentConfig.id, mediaPaths);
4694
5252
  }
5253
+ /** The route has already authorized this API principal for the Agent. Preserve
5254
+ * the original channel binding, but never impersonate its human sender. */
5255
+ async sendOrchestratedChannel(rawChatId, channel, sessionId, message, senderName, callbacks, opts) {
5256
+ if (!opts.principalId)
5257
+ throw new Error('Authenticated principal required for conversation orchestration');
5258
+ const runtime = await this.getOrchestration();
5259
+ const rows = runtime.store.all('SELECT * FROM conversations WHERE agent_session_id=?', sessionId);
5260
+ if (rows.length > 1 || rows.some(row => row.source !== channel || row.chat_id !== rawChatId))
5261
+ throw new Error('Ambiguous or mismatched channel session');
5262
+ if (!rows.length) {
5263
+ const index = await this.sessionStore.loadIndex(this.agentConfig.id, rawChatId, channel);
5264
+ if (!index?.sessions.some(session => session.id === sessionId))
5265
+ throw new Error('Session not found');
5266
+ }
5267
+ const requestId = opts.requestId ?? (0, crypto_1.randomUUID)();
5268
+ const accepted = runtime.submitInput({ scope: { agentId: this.agentConfig.id, agentSessionId: sessionId,
5269
+ source: channel, accountId: String(rows[0]?.account_id ?? this.agentConfig.id), chatId: rawChatId,
5270
+ threadKey: String(rows[0]?.thread_key ?? ''), principalId: opts.principalId },
5271
+ text: message, requestId, trustedChannelMember: true, metadata: { senderName, senderId: opts.principalId } }, { execute: opts.allowTools ?? false, writeMemory: false });
5272
+ const turn = this.turnStreams.start((0, turn_stream_1.turnStreamKey)(channel, sessionId), requestId);
5273
+ const sink = (0, turn_stream_1.callbackSink)(callbacks);
5274
+ turn.attach(sink, 0);
5275
+ const displayed = new Map();
5276
+ const unsubscribe = runtime.subscribeText(sessionId, opts.principalId, event => {
5277
+ if (!runtime.responseBelongsToInput(accepted.inputId, event.responseId))
5278
+ return;
5279
+ const previous = displayed.get(event.responseId) ?? '';
5280
+ if (event.text.startsWith(previous)) {
5281
+ const delta = event.text.slice(previous.length);
5282
+ displayed.set(event.responseId, event.text);
5283
+ if (delta)
5284
+ turn.emit({ type: 'text_delta', text: delta });
5285
+ }
5286
+ });
5287
+ const input = runtime.store.get('SELECT conversation_id FROM conversation_inputs WHERE id=?', accepted.inputId);
5288
+ const conversation = runtime.store.assertMember(String(input.conversation_id), opts.principalId);
5289
+ let cursor = { streamId: String(conversation.stream_id), seq: Number(conversation.last_event_seq) };
5290
+ const events = runtime.events.subscribe(String(conversation.id), opts.principalId, cursor);
5291
+ let closed = false;
5292
+ let timer;
5293
+ const close = () => { closed = true; clearTimeout(timer); unsubscribe(); events.close(); };
5294
+ const forward = (page) => {
5295
+ if (page.snapshotRequired)
5296
+ throw new Error('Tool activity history expired; reload session activity');
5297
+ for (const event of page.events) {
5298
+ if (event.seq <= cursor.seq)
5299
+ continue;
5300
+ const payload = event.payload;
5301
+ if (event.type === 'tool.activity' && payload.type === 'tool_use')
5302
+ turn.emit({ ...payload, type: 'tool_use' });
5303
+ cursor = { streamId: page.cursor.streamId, seq: event.seq };
5304
+ }
5305
+ };
5306
+ const drain = () => {
5307
+ while (true) {
5308
+ const page = runtime.events.read(String(conversation.id), opts.principalId, cursor);
5309
+ forward(page);
5310
+ if (!page.snapshotRequired && !page.events.length)
5311
+ break;
5312
+ }
5313
+ };
5314
+ const fail = (error) => {
5315
+ if (closed)
5316
+ return;
5317
+ try {
5318
+ drain();
5319
+ }
5320
+ catch { /* Preserve the original stream/provider failure. */ }
5321
+ this.turnStreams.completeAndRelease(turn, (0, turn_stream_1.errorEvent)(error), error);
5322
+ close();
5323
+ };
5324
+ void (async () => {
5325
+ for await (const page of events.pages) {
5326
+ if (closed)
5327
+ break;
5328
+ forward(page);
5329
+ }
5330
+ })().catch(fail);
5331
+ timer = setTimeout(() => fail(Object.assign(new Error('Agent response timeout'), { code: 'TIMEOUT_SOFT' })), opts.timeoutMs);
5332
+ void accepted.response.then(text => {
5333
+ if (closed)
5334
+ return;
5335
+ try {
5336
+ // Drain before terminal success, even when completion precedes the next poll.
5337
+ drain();
5338
+ this.turnStreams.completeAndRelease(turn, (0, turn_stream_1.resultEvent)(text, []));
5339
+ close();
5340
+ }
5341
+ catch (error) {
5342
+ fail(error instanceof Error ? error : new Error('Tool activity stream failed'));
5343
+ }
5344
+ }, fail);
5345
+ return () => { turn.detach(sink); }; // Durable work and original channel delivery survive disconnect.
5346
+ }
4695
5347
  /**
4696
5348
  * Send a message into an existing channel session (cross-channel continuation from UI).
4697
5349
  * The session process receives full history context from the session JSON (Layer 1).
@@ -4701,6 +5353,10 @@ class AgentRunner extends events_1.EventEmitter {
4701
5353
  * parser — and writing here as well produced a second row per turn. See done().
4702
5354
  */
4703
5355
  async sendMessageToSession(rawChatId, channel, sessionId, message, senderName, callbacks, opts) {
5356
+ const managed = (this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(channel)) ||
5357
+ (this.orchestration?.ownsSession(sessionId) && !this.orchestration.canReturnToLegacy());
5358
+ if (managed)
5359
+ return this.sendOrchestratedChannel(rawChatId, channel, sessionId, message, senderName, callbacks, opts);
4704
5360
  // Ensure the session process uses the correct channel source
4705
5361
  this.channelSourceMap.set(rawChatId, channel);
4706
5362
  // Channel sessions use agent-level model (not per-session)