@0xmaxma/claude-gateway 1.8.14 → 2.0.1

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 (586) hide show
  1. package/README.md +28 -1613
  2. package/config.template.json +7 -1
  3. package/dist/agent/runner.d.ts +419 -0
  4. package/dist/agent/runner.d.ts.map +1 -1
  5. package/dist/agent/runner.js +736 -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 +162 -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 +260 -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 +151 -0
  166. package/dist/orchestration/decisions.js.map +1 -0
  167. package/dist/orchestration/delivery.d.ts +34 -0
  168. package/dist/orchestration/delivery.d.ts.map +1 -0
  169. package/dist/orchestration/delivery.js +235 -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 +237 -0
  252. package/dist/orchestration/runtime.d.ts.map +1 -0
  253. package/dist/orchestration/runtime.js +1031 -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 +92 -0
  272. package/dist/orchestration/store.d.ts.map +1 -0
  273. package/dist/orchestration/store.js +326 -0
  274. package/dist/orchestration/store.js.map +1 -0
  275. package/dist/orchestration/task-controls.d.ts +172 -0
  276. package/dist/orchestration/task-controls.d.ts.map +1 -0
  277. package/dist/orchestration/task-controls.js +59 -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-questions.d.ts +26 -0
  284. package/dist/orchestration/task-questions.d.ts.map +1 -0
  285. package/dist/orchestration/task-questions.js +160 -0
  286. package/dist/orchestration/task-questions.js.map +1 -0
  287. package/dist/orchestration/task-report.d.ts +7 -0
  288. package/dist/orchestration/task-report.d.ts.map +1 -0
  289. package/dist/orchestration/task-report.js +33 -0
  290. package/dist/orchestration/task-report.js.map +1 -0
  291. package/dist/orchestration/tasks/checkpoint-hook.d.ts +19 -0
  292. package/dist/orchestration/tasks/checkpoint-hook.d.ts.map +1 -0
  293. package/dist/orchestration/tasks/checkpoint-hook.js +49 -0
  294. package/dist/orchestration/tasks/checkpoint-hook.js.map +1 -0
  295. package/dist/orchestration/tasks/cleanup.d.ts +18 -0
  296. package/dist/orchestration/tasks/cleanup.d.ts.map +1 -0
  297. package/dist/orchestration/tasks/cleanup.js +113 -0
  298. package/dist/orchestration/tasks/cleanup.js.map +1 -0
  299. package/dist/orchestration/tasks/driver.d.ts +23 -0
  300. package/dist/orchestration/tasks/driver.d.ts.map +1 -0
  301. package/dist/orchestration/tasks/driver.js +209 -0
  302. package/dist/orchestration/tasks/driver.js.map +1 -0
  303. package/dist/orchestration/tasks/failure.d.ts +5 -0
  304. package/dist/orchestration/tasks/failure.d.ts.map +1 -0
  305. package/dist/orchestration/tasks/failure.js +17 -0
  306. package/dist/orchestration/tasks/failure.js.map +1 -0
  307. package/dist/orchestration/tasks/pool.d.ts +18 -0
  308. package/dist/orchestration/tasks/pool.d.ts.map +1 -0
  309. package/dist/orchestration/tasks/pool.js +73 -0
  310. package/dist/orchestration/tasks/pool.js.map +1 -0
  311. package/dist/orchestration/tasks/scheduler.d.ts +38 -0
  312. package/dist/orchestration/tasks/scheduler.d.ts.map +1 -0
  313. package/dist/orchestration/tasks/scheduler.js +160 -0
  314. package/dist/orchestration/tasks/scheduler.js.map +1 -0
  315. package/dist/orchestration/tasks/service.d.ts +114 -0
  316. package/dist/orchestration/tasks/service.d.ts.map +1 -0
  317. package/dist/orchestration/tasks/service.js +760 -0
  318. package/dist/orchestration/tasks/service.js.map +1 -0
  319. package/dist/orchestration/tasks/shared-workspace.d.ts +25 -0
  320. package/dist/orchestration/tasks/shared-workspace.d.ts.map +1 -0
  321. package/dist/orchestration/tasks/shared-workspace.js +162 -0
  322. package/dist/orchestration/tasks/shared-workspace.js.map +1 -0
  323. package/dist/orchestration/tasks/task-directive.d.ts +6 -0
  324. package/dist/orchestration/tasks/task-directive.d.ts.map +1 -0
  325. package/dist/orchestration/tasks/task-directive.js +41 -0
  326. package/dist/orchestration/tasks/task-directive.js.map +1 -0
  327. package/dist/orchestration/tasks/timing.d.ts +15 -0
  328. package/dist/orchestration/tasks/timing.d.ts.map +1 -0
  329. package/dist/orchestration/tasks/timing.js +48 -0
  330. package/dist/orchestration/tasks/timing.js.map +1 -0
  331. package/dist/orchestration/tasks/tool-repetition.d.ts +3 -0
  332. package/dist/orchestration/tasks/tool-repetition.d.ts.map +1 -0
  333. package/dist/orchestration/tasks/tool-repetition.js +30 -0
  334. package/dist/orchestration/tasks/tool-repetition.js.map +1 -0
  335. package/dist/orchestration/tasks/workspace.d.ts +24 -0
  336. package/dist/orchestration/tasks/workspace.d.ts.map +1 -0
  337. package/dist/orchestration/tasks/workspace.js +147 -0
  338. package/dist/orchestration/tasks/workspace.js.map +1 -0
  339. package/dist/orchestration/telegram-speech.d.ts +17 -0
  340. package/dist/orchestration/telegram-speech.d.ts.map +1 -0
  341. package/dist/orchestration/telegram-speech.js +55 -0
  342. package/dist/orchestration/telegram-speech.js.map +1 -0
  343. package/dist/orchestration/telegram-tool-status.d.ts +23 -0
  344. package/dist/orchestration/telegram-tool-status.d.ts.map +1 -0
  345. package/dist/orchestration/telegram-tool-status.js +207 -0
  346. package/dist/orchestration/telegram-tool-status.js.map +1 -0
  347. package/dist/orchestration/telegram-voices.d.ts +42 -0
  348. package/dist/orchestration/telegram-voices.d.ts.map +1 -0
  349. package/dist/orchestration/telegram-voices.js +70 -0
  350. package/dist/orchestration/telegram-voices.js.map +1 -0
  351. package/dist/orchestration/tool-activity.d.ts +10 -0
  352. package/dist/orchestration/tool-activity.d.ts.map +1 -0
  353. package/dist/orchestration/tool-activity.js +38 -0
  354. package/dist/orchestration/tool-activity.js.map +1 -0
  355. package/dist/orchestration/types.d.ts +177 -0
  356. package/dist/orchestration/types.d.ts.map +1 -0
  357. package/dist/orchestration/types.js +13 -0
  358. package/dist/orchestration/types.js.map +1 -0
  359. package/dist/orchestration/voice-reply-policy.d.ts +8 -0
  360. package/dist/orchestration/voice-reply-policy.d.ts.map +1 -0
  361. package/dist/orchestration/voice-reply-policy.js +46 -0
  362. package/dist/orchestration/voice-reply-policy.js.map +1 -0
  363. package/dist/orchestration/worker-shares.d.ts +8 -0
  364. package/dist/orchestration/worker-shares.d.ts.map +1 -0
  365. package/dist/orchestration/worker-shares.js +59 -0
  366. package/dist/orchestration/worker-shares.js.map +1 -0
  367. package/dist/orchestration/workflow.d.ts +121 -0
  368. package/dist/orchestration/workflow.d.ts.map +1 -0
  369. package/dist/orchestration/workflow.js +69 -0
  370. package/dist/orchestration/workflow.js.map +1 -0
  371. package/dist/session/input-image.d.ts +10 -0
  372. package/dist/session/input-image.d.ts.map +1 -0
  373. package/dist/session/input-image.js +3 -0
  374. package/dist/session/input-image.js.map +1 -0
  375. package/dist/session/process.d.ts +15 -3
  376. package/dist/session/process.d.ts.map +1 -1
  377. package/dist/session/process.js +143 -35
  378. package/dist/session/process.js.map +1 -1
  379. package/dist/session/runtime-profile.d.ts +36 -0
  380. package/dist/session/runtime-profile.d.ts.map +1 -0
  381. package/dist/session/runtime-profile.js +63 -0
  382. package/dist/session/runtime-profile.js.map +1 -0
  383. package/dist/session/store.d.ts.map +1 -1
  384. package/dist/session/store.js +23 -1
  385. package/dist/session/store.js.map +1 -1
  386. package/dist/share/share-store.d.ts +3 -1
  387. package/dist/share/share-store.d.ts.map +1 -1
  388. package/dist/share/share-store.js +13 -2
  389. package/dist/share/share-store.js.map +1 -1
  390. package/dist/shared/line-request-order.d.ts +2 -0
  391. package/dist/shared/line-request-order.d.ts.map +1 -0
  392. package/dist/shared/line-request-order.js +16 -0
  393. package/dist/shared/line-request-order.js.map +1 -0
  394. package/dist/shared/task-elapsed.d.ts +3 -0
  395. package/dist/shared/task-elapsed.d.ts.map +1 -0
  396. package/dist/shared/task-elapsed.js +12 -0
  397. package/dist/shared/task-elapsed.js.map +1 -0
  398. package/dist/shared/task-updated-time.d.ts +3 -0
  399. package/dist/shared/task-updated-time.d.ts.map +1 -0
  400. package/dist/shared/task-updated-time.js +15 -0
  401. package/dist/shared/task-updated-time.js.map +1 -0
  402. package/dist/skills/loader.d.ts +2 -0
  403. package/dist/skills/loader.d.ts.map +1 -1
  404. package/dist/skills/loader.js.map +1 -1
  405. package/dist/telegram/chunks.d.ts +19 -0
  406. package/dist/telegram/chunks.d.ts.map +1 -0
  407. package/dist/telegram/chunks.js +129 -0
  408. package/dist/telegram/chunks.js.map +1 -0
  409. package/dist/telegram/markdown.d.ts.map +1 -1
  410. package/dist/telegram/markdown.js +18 -3
  411. package/dist/telegram/markdown.js.map +1 -1
  412. package/dist/telegram/receiver.d.ts +2 -1
  413. package/dist/telegram/receiver.d.ts.map +1 -1
  414. package/dist/telegram/receiver.js +6 -1
  415. package/dist/telegram/receiver.js.map +1 -1
  416. package/dist/types.d.ts +14 -0
  417. package/dist/types.d.ts.map +1 -1
  418. package/dist/ui/web-ui.d.ts.map +1 -1
  419. package/dist/ui/web-ui.js +31 -5
  420. package/dist/ui/web-ui.js.map +1 -1
  421. package/dist/voice/diagnostics.d.ts +29 -0
  422. package/dist/voice/diagnostics.d.ts.map +1 -0
  423. package/dist/voice/diagnostics.js +37 -0
  424. package/dist/voice/diagnostics.js.map +1 -0
  425. package/dist/voice/errors.d.ts +19 -0
  426. package/dist/voice/errors.d.ts.map +1 -0
  427. package/dist/voice/errors.js +133 -0
  428. package/dist/voice/errors.js.map +1 -0
  429. package/dist/voice/line-audio.d.ts +4 -0
  430. package/dist/voice/line-audio.d.ts.map +1 -0
  431. package/dist/voice/line-audio.js +27 -0
  432. package/dist/voice/line-audio.js.map +1 -0
  433. package/dist/voice/managed-quota.d.ts +5 -0
  434. package/dist/voice/managed-quota.d.ts.map +1 -0
  435. package/dist/voice/managed-quota.js +35 -0
  436. package/dist/voice/managed-quota.js.map +1 -0
  437. package/dist/voice/mp3.d.ts +3 -0
  438. package/dist/voice/mp3.d.ts.map +1 -0
  439. package/dist/voice/mp3.js +30 -0
  440. package/dist/voice/mp3.js.map +1 -0
  441. package/dist/voice/notes.d.ts +9 -0
  442. package/dist/voice/notes.d.ts.map +1 -0
  443. package/dist/voice/notes.js +68 -0
  444. package/dist/voice/notes.js.map +1 -0
  445. package/dist/voice/playback.d.ts +22 -0
  446. package/dist/voice/playback.d.ts.map +1 -0
  447. package/dist/voice/playback.js +52 -0
  448. package/dist/voice/playback.js.map +1 -0
  449. package/dist/voice/preview.d.ts +6 -0
  450. package/dist/voice/preview.d.ts.map +1 -0
  451. package/dist/voice/preview.js +44 -0
  452. package/dist/voice/preview.js.map +1 -0
  453. package/dist/voice/protocol.d.ts +10 -0
  454. package/dist/voice/protocol.d.ts.map +1 -0
  455. package/dist/voice/protocol.js +30 -0
  456. package/dist/voice/protocol.js.map +1 -0
  457. package/dist/voice/providers/cartesia-tts.d.ts +26 -0
  458. package/dist/voice/providers/cartesia-tts.d.ts.map +1 -0
  459. package/dist/voice/providers/cartesia-tts.js +95 -0
  460. package/dist/voice/providers/cartesia-tts.js.map +1 -0
  461. package/dist/voice/providers/deepgram-stt.d.ts +20 -0
  462. package/dist/voice/providers/deepgram-stt.d.ts.map +1 -0
  463. package/dist/voice/providers/deepgram-stt.js +66 -0
  464. package/dist/voice/providers/deepgram-stt.js.map +1 -0
  465. package/dist/voice/providers/elevenlabs-stt.d.ts +20 -0
  466. package/dist/voice/providers/elevenlabs-stt.d.ts.map +1 -0
  467. package/dist/voice/providers/elevenlabs-stt.js +92 -0
  468. package/dist/voice/providers/elevenlabs-stt.js.map +1 -0
  469. package/dist/voice/providers/elevenlabs-tts.d.ts +26 -0
  470. package/dist/voice/providers/elevenlabs-tts.d.ts.map +1 -0
  471. package/dist/voice/providers/elevenlabs-tts.js +82 -0
  472. package/dist/voice/providers/elevenlabs-tts.js.map +1 -0
  473. package/dist/voice/providers/encoded-audio-tts.d.ts +21 -0
  474. package/dist/voice/providers/encoded-audio-tts.d.ts.map +1 -0
  475. package/dist/voice/providers/encoded-audio-tts.js +175 -0
  476. package/dist/voice/providers/encoded-audio-tts.js.map +1 -0
  477. package/dist/voice/providers/fake.d.ts +39 -0
  478. package/dist/voice/providers/fake.d.ts.map +1 -0
  479. package/dist/voice/providers/fake.js +41 -0
  480. package/dist/voice/providers/fake.js.map +1 -0
  481. package/dist/voice/providers/gemini-live-stt.d.ts +21 -0
  482. package/dist/voice/providers/gemini-live-stt.d.ts.map +1 -0
  483. package/dist/voice/providers/gemini-live-stt.js +158 -0
  484. package/dist/voice/providers/gemini-live-stt.js.map +1 -0
  485. package/dist/voice/providers/gemini.d.ts +60 -0
  486. package/dist/voice/providers/gemini.d.ts.map +1 -0
  487. package/dist/voice/providers/gemini.js +124 -0
  488. package/dist/voice/providers/gemini.js.map +1 -0
  489. package/dist/voice/providers/model-ref.d.ts +9 -0
  490. package/dist/voice/providers/model-ref.d.ts.map +1 -0
  491. package/dist/voice/providers/model-ref.js +31 -0
  492. package/dist/voice/providers/model-ref.js.map +1 -0
  493. package/dist/voice/providers/openrouter.d.ts +39 -0
  494. package/dist/voice/providers/openrouter.d.ts.map +1 -0
  495. package/dist/voice/providers/openrouter.js +154 -0
  496. package/dist/voice/providers/openrouter.js.map +1 -0
  497. package/dist/voice/providers/paxalabs-stt.d.ts +12 -0
  498. package/dist/voice/providers/paxalabs-stt.d.ts.map +1 -0
  499. package/dist/voice/providers/paxalabs-stt.js +41 -0
  500. package/dist/voice/providers/paxalabs-stt.js.map +1 -0
  501. package/dist/voice/providers/paxalabs-tts.d.ts +23 -0
  502. package/dist/voice/providers/paxalabs-tts.d.ts.map +1 -0
  503. package/dist/voice/providers/paxalabs-tts.js +80 -0
  504. package/dist/voice/providers/paxalabs-tts.js.map +1 -0
  505. package/dist/voice/providers/recorded-stt.d.ts +34 -0
  506. package/dist/voice/providers/recorded-stt.d.ts.map +1 -0
  507. package/dist/voice/providers/recorded-stt.js +59 -0
  508. package/dist/voice/providers/recorded-stt.js.map +1 -0
  509. package/dist/voice/providers/registry.d.ts +10 -0
  510. package/dist/voice/providers/registry.d.ts.map +1 -0
  511. package/dist/voice/providers/registry.js +77 -0
  512. package/dist/voice/providers/registry.js.map +1 -0
  513. package/dist/voice/providers/socket.d.ts +10 -0
  514. package/dist/voice/providers/socket.d.ts.map +1 -0
  515. package/dist/voice/providers/socket.js +91 -0
  516. package/dist/voice/providers/socket.js.map +1 -0
  517. package/dist/voice/providers/upstream.d.ts +9 -0
  518. package/dist/voice/providers/upstream.d.ts.map +1 -0
  519. package/dist/voice/providers/upstream.js +51 -0
  520. package/dist/voice/providers/upstream.js.map +1 -0
  521. package/dist/voice/providers/voice-catalog.d.ts +17 -0
  522. package/dist/voice/providers/voice-catalog.d.ts.map +1 -0
  523. package/dist/voice/providers/voice-catalog.js +167 -0
  524. package/dist/voice/providers/voice-catalog.js.map +1 -0
  525. package/dist/voice/queue.d.ts +2 -0
  526. package/dist/voice/queue.d.ts.map +1 -0
  527. package/dist/voice/queue.js +6 -0
  528. package/dist/voice/queue.js.map +1 -0
  529. package/dist/voice/session.d.ts +86 -0
  530. package/dist/voice/session.d.ts.map +1 -0
  531. package/dist/voice/session.js +464 -0
  532. package/dist/voice/session.js.map +1 -0
  533. package/dist/voice/turn-manager.d.ts +27 -0
  534. package/dist/voice/turn-manager.d.ts.map +1 -0
  535. package/dist/voice/turn-manager.js +75 -0
  536. package/dist/voice/turn-manager.js.map +1 -0
  537. package/dist/voice/types.d.ts +78 -0
  538. package/dist/voice/types.d.ts.map +1 -0
  539. package/dist/voice/types.js +18 -0
  540. package/dist/voice/types.js.map +1 -0
  541. package/dist/voice/wav.d.ts +2 -0
  542. package/dist/voice/wav.d.ts.map +1 -0
  543. package/dist/voice/wav.js +21 -0
  544. package/dist/voice/wav.js.map +1 -0
  545. package/dist/wechat/ilink-client.d.ts +13 -0
  546. package/dist/wechat/ilink-client.d.ts.map +1 -1
  547. package/dist/wechat/ilink-client.js +19 -0
  548. package/dist/wechat/ilink-client.js.map +1 -1
  549. package/dist/whatsapp/manager.d.ts.map +1 -1
  550. package/dist/whatsapp/manager.js +51 -1
  551. package/dist/whatsapp/manager.js.map +1 -1
  552. package/mcp/capability-catalog.ts +206 -0
  553. package/mcp/modules.ts +56 -0
  554. package/mcp/server.ts +18 -37
  555. package/mcp/tools/browser/module.ts +1 -1
  556. package/mcp/tools/browser/skills/open-browser/SKILL.md +5 -3
  557. package/mcp/tools/discord/commands.ts +17 -1
  558. package/mcp/tools/discord/inbound.ts +11 -0
  559. package/mcp/tools/discord/module.ts +42 -2
  560. package/mcp/tools/discord/receiver-server.ts +10 -0
  561. package/mcp/tools/discord/types.ts +4 -1
  562. package/mcp/tools/image/module.ts +3 -1
  563. package/mcp/tools/receiver-spool.test.ts +28 -0
  564. package/mcp/tools/receiver-spool.ts +56 -0
  565. package/mcp/tools/share-file/module.ts +9 -1
  566. package/mcp/tools/shared/share-client.ts +19 -0
  567. package/mcp/tools/tasks/module.ts +35 -0
  568. package/mcp/tools/telegram/media-group.ts +21 -0
  569. package/mcp/tools/telegram/receiver-server.ts +246 -11
  570. package/mcp/tools/telegram/selection-confirmation.ts +20 -0
  571. package/mcp/tools/telegram/task-browser.ts +117 -0
  572. package/mcp/tools/telegram/task-detail.ts +36 -0
  573. package/mcp/tools/telegram/task-elapsed.ts +1 -0
  574. package/mcp/tools/telegram/typing.ts +6 -116
  575. package/mcp/tools/video/module.ts +1 -1
  576. package/mcp/types.ts +5 -0
  577. package/package.json +2 -1
  578. package/resource/claude_gateway.jpeg +0 -0
  579. package/scripts/orchestration/bench-store.cjs +30 -0
  580. package/scripts/orchestration/inspect.cjs +16 -0
  581. package/scripts/orchestration/probe-profile.cjs +67 -0
  582. package/scripts/orchestration/reconcile.cjs +21 -0
  583. package/scripts/orchestration/smoke-elevenlabs.cjs +90 -0
  584. package/scripts/orchestration/smoke-media.cjs +70 -0
  585. package/scripts/orchestration/smoke-text.cjs +101 -0
  586. 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,237 @@ 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 (!['telegram', '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}` : meta.message_thread_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
+ async answerApiTask(sessionId, principalId, taskId, questionId, answer) {
400
+ if (!this.agentConfig.orchestration?.enabled)
401
+ throw new Error('ORCHESTRATION_DISABLED');
402
+ const runtime = await this.getOrchestration();
403
+ const task = runtime.taskControls.answer(sessionId, principalId, taskId, questionId, answer);
404
+ await runtime.flushHistory();
405
+ return task;
406
+ }
407
+ stopVoiceResponse(sessionId) { this.orchestration?.stopResponse(sessionId); }
408
+ async sendOrchestratedApi(sessionId, chatId, message, opts, onText, onTool) {
409
+ const semantic = this.agentConfig.orchestration?.conversation?.semanticIntake === true;
410
+ if (!semantic && this.pendingApiSessions.has(sessionId))
411
+ throw Object.assign(new Error('Session already has a pending request'), { code: 'CONFLICT' });
412
+ if (!opts.principalId)
413
+ throw new Error('Authenticated principal required for conversation orchestration');
414
+ const deadline = Date.now() + opts.timeoutMs;
415
+ if (!semantic)
416
+ this.pendingApiSessions.add(sessionId); // reserve before the first await
417
+ try {
418
+ const orchestration = await this.getOrchestration();
419
+ await this.sessionStore.ensureApiSession(this.agentConfig.id, chatId, sessionId);
420
+ this.apiChatIds.set(sessionId, chatId);
421
+ const media = opts.mediaFiles?.length ? await promoteUiUploads(this.agentsBaseDir, this.agentConfig.id, sessionId, opts.mediaFiles, this.logger) : [];
422
+ const index = await this.sessionStore.loadIndex(this.agentConfig.id, chatId, 'api');
423
+ const saved = index?.sessions.find(session => session.id === sessionId)?.imageConfig;
424
+ const selected = { ...(saved ? AgentRunner.durableImageConfig(saved) : {}), ...opts.imageParams };
425
+ const imageParams = AgentRunner.remapImageParamsRefs(Object.keys(selected).length ? selected : undefined, opts.mediaFiles, media);
426
+ const videoParams = AgentRunner.remapVideoParamsRefs(opts.videoParams, opts.mediaFiles, media);
427
+ if (videoParams)
428
+ await this.sessionStore.updateSessionMeta(this.agentConfig.id, chatId, sessionId, { videoConfig: videoParams }, 'api');
429
+ const imageConfig = opts.imageParams && imageParams ? AgentRunner.durableImageConfig(imageParams) : undefined;
430
+ if (imageConfig)
431
+ await this.sessionStore.updateSessionMeta(this.agentConfig.id, chatId, sessionId, { imageConfig }, 'api');
432
+ const requestId = opts.requestId ?? (0, crypto_1.randomUUID)();
433
+ const scopedInput = { scope: { agentId: this.agentConfig.id, agentSessionId: sessionId, source: 'api', accountId: opts.principalId,
434
+ chatId, threadKey: '', principalId: opts.principalId }, text: message || '[Attachment inspection requested]', attachmentIds: media,
435
+ requestId, storeUserMessage: !opts.skipUserMessage,
436
+ metadata: imageParams || videoParams ? { promptContext: (imageParams ? AgentRunner.buildImageParamsNote(imageParams) : '') + (videoParams ? AgentRunner.buildVideoParamsNote(videoParams) : ''), imageRefs: imageParams?.image_refs } : undefined };
437
+ let text;
438
+ if (semantic) {
439
+ const accepted = orchestration.submitInput({ ...scopedInput, model: opts.model }, { execute: opts.allowTools ?? false, writeMemory: false }, onTool);
440
+ const lengths = new Map();
441
+ const unsubscribe = orchestration.subscribeText(sessionId, opts.principalId, event => {
442
+ if (!orchestration.responseBelongsToInput(accepted.inputId, event.responseId) || !event.text)
443
+ return;
444
+ const before = lengths.get(event.responseId) ?? 0;
445
+ if (event.text.length > before)
446
+ onText?.(event.text.slice(before));
447
+ lengths.set(event.responseId, event.text.length);
448
+ });
449
+ try {
450
+ text = await accepted.response;
451
+ }
452
+ finally {
453
+ unsubscribe();
454
+ }
455
+ }
456
+ else
457
+ text = await orchestration.send(scopedInput, { execute: opts.allowTools ?? false, writeMemory: false }, { timeoutMs: opts.timeoutMs, model: opts.model, onText, onTool });
458
+ if (opts.waitForTasks)
459
+ text = await orchestration.waitForTaskReport(sessionId, opts.principalId, requestId, text, deadline);
460
+ this.addApiAttachments(sessionId, orchestration.responseFiles(sessionId, requestId).map(ref => media_store_1.MediaStore.resolvePath(this.agentsBaseDir, this.agentConfig.id, ref)));
461
+ return { text, attachments: this.popApiAttachments(sessionId) };
462
+ }
463
+ finally {
464
+ if (!semantic)
465
+ this.pendingApiSessions.delete(sessionId);
466
+ }
467
+ }
229
468
  constructor(agentConfig, gatewayConfig, logger) {
230
469
  super();
231
470
  this.stopping = false;
@@ -246,6 +485,7 @@ class AgentRunner extends events_1.EventEmitter {
246
485
  this.slackThreadTs = new Map();
247
486
  this.receiver = null;
248
487
  this.discordReceiver = null;
488
+ this.discordCommandConfig = '';
249
489
  // LINE slow-LLM postback button manager (null when LINE disabled or threshold=0).
250
490
  this.lineReply = null;
251
491
  // Slack has no reply-token TTL to work around (see the plan's "no reply-manager
@@ -357,6 +597,7 @@ class AgentRunner extends events_1.EventEmitter {
357
597
  this.apiChatIds = new Map();
358
598
  // Skill registry for detecting /skill-name commands in user messages
359
599
  this.skillRegistry = { skills: new Map() };
600
+ this.skillNotificationOrigins = new Map();
360
601
  // Cancel function for the daily history cleanup timer
361
602
  this.cancelCleanup = null;
362
603
  /**
@@ -399,7 +640,8 @@ class AgentRunner extends events_1.EventEmitter {
399
640
  }
400
641
  });
401
642
  };
402
- this.agentConfig = agentConfig;
643
+ this.telegramCommandConfig = '';
644
+ this.agentConfig = (0, gateway_config_1.applyGatewayOrchestration)(agentConfig, gatewayConfig);
403
645
  this.gatewayConfig = gatewayConfig;
404
646
  this.logger = logger ?? (0, logger_1.createLogger)(agentConfig.id, gatewayConfig.gateway.logDir);
405
647
  // Resolve agentsBaseDir: workspace is at <agentsBaseDir>/<agentId>/workspace
@@ -428,6 +670,25 @@ class AgentRunner extends events_1.EventEmitter {
428
670
  return this.skillRegistry;
429
671
  }
430
672
  /** Wire the skill-learning manager (planning-62). Optional; unset = feature off. */
673
+ async notifySkillLearning(sessionId, text) {
674
+ if (this.orchestration?.ownsSession(sessionId)) {
675
+ const conversation = this.orchestration.store.get('SELECT source,chat_id FROM conversations WHERE agent_session_id=?', sessionId);
676
+ const source = conversation?.source;
677
+ const index = source && source !== 'api' ? await this.sessionStore.loadIndex(this.agentConfig.id, String(conversation.chat_id), source) : undefined;
678
+ await this.orchestration.notifySession(sessionId, text, source === 'api' || index?.activeSessionId === sessionId);
679
+ return;
680
+ }
681
+ const origin = this.skillNotificationOrigins.get(sessionId);
682
+ if (!origin)
683
+ return; // Diary only when the original destination is unavailable.
684
+ this.historyDb.insertMessage({ chatId: `${origin.source}-${origin.chatId}`, sessionId, source: origin.source, role: 'assistant', content: text, ts: Date.now() });
685
+ const index = await this.sessionStore.loadIndex(this.agentConfig.id, origin.chatId, origin.source);
686
+ if (index?.activeSessionId !== sessionId)
687
+ return;
688
+ 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)());
689
+ if (result.state !== 'delivered')
690
+ this.logger.warn('Skill notification delivery failed', { sessionId, code: result.code });
691
+ }
431
692
  setSkillLearning(manager) {
432
693
  this.skillLearning = manager;
433
694
  }
@@ -453,7 +714,7 @@ class AgentRunner extends events_1.EventEmitter {
453
714
  req.on('data', (chunk) => {
454
715
  raw += chunk;
455
716
  });
456
- req.on('end', () => {
717
+ req.on('end', async () => {
457
718
  if (url.pathname === '/command') {
458
719
  this.handleCommandRequest(raw, res);
459
720
  return;
@@ -471,8 +732,6 @@ class AgentRunner extends events_1.EventEmitter {
471
732
  // receiver (Bun) can try to reuse the stale socket → "socket connection
472
733
  // was closed unexpectedly". Closing per-request avoids the race entirely.
473
734
  res.setHeader('Connection', 'close');
474
- res.writeHead(200);
475
- res.end('ok');
476
735
  try {
477
736
  const params = JSON.parse(raw);
478
737
  const meta = params.meta ?? {};
@@ -486,6 +745,87 @@ class AgentRunner extends events_1.EventEmitter {
486
745
  // forget adding to a chain of `=== 'x' ? 'x' : ...` checks.
487
746
  const channelSource = ((0, types_1.isChatChannel)(meta['source']) ? meta['source'] : 'telegram');
488
747
  this.channelSourceMap.set(chatId, channelSource);
748
+ const channelOrchestration = this.orchestration?.ownsChannel(channelSource, chatId) || (this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(channelSource));
749
+ if (channelSource !== 'telegram' && this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(channelSource) && /^\/sessions?(?:\s|$)/.test(content.trim())) {
750
+ const index = await this.sessionStore.listSessions(this.agentConfig.id, chatId, channelSource);
751
+ const current = index.sessions.find(session => session.id === index.activeSessionId);
752
+ const text = content.trim() === '/sessions'
753
+ ? `Sessions\n${index.sessions.slice(0, 15).map(session => `${session.id === index.activeSessionId ? '✅ ' : ''}${session.name}\n${session.id}`).join('\n\n')}`
754
+ : `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`;
755
+ await this.sendOrchestrationControl(channelSource, chatId, { text, buttons: [] }, meta);
756
+ res.writeHead(200);
757
+ res.end('ok');
758
+ return;
759
+ }
760
+ if (/^\/task_question(?:\s|$)/.test(content.trim()) || (channelSource !== 'telegram' && /^\/orch\s+q:/.test(content.trim()))) {
761
+ let menu = { text: 'Task controls require orchestration mode.', buttons: [] };
762
+ if (channelOrchestration) {
763
+ const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, channelSource);
764
+ const runtime = await this.getOrchestration();
765
+ const scope = { channel: channelSource, chatId, thread: channelSource === 'whatsapp' && meta.account_id ? `whatsapp-account:${meta.account_id}` : meta.thread_ts ?? meta.message_thread_id ?? '', sessionId, principalId: `${channelSource}:${meta.user_id ?? meta.user ?? chatId}` };
766
+ try {
767
+ menu = runtime.questionControls.handle(scope, content.trim()) ?? { text: 'Invalid task question command.', buttons: [] };
768
+ }
769
+ catch {
770
+ menu = { text: 'Question unavailable or already answered. Use /tasks to refresh.', buttons: [] };
771
+ }
772
+ await runtime.flushHistory();
773
+ }
774
+ const responseMeta = { ...meta };
775
+ delete responseMeta.control_message_id;
776
+ await this.sendOrchestrationControl(channelSource, chatId, menu, responseMeta);
777
+ res.writeHead(200);
778
+ res.end('ok');
779
+ return;
780
+ }
781
+ 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'))) {
782
+ const enabled = !!(this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(channelSource));
783
+ if (enabled) {
784
+ const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, channelSource);
785
+ const runtime = await this.getOrchestration();
786
+ const scope = { channel: channelSource, chatId, thread: meta.thread_ts ?? meta.message_thread_id ?? '', sessionId, principalId: `${channelSource}:${meta.user_id ?? meta.user ?? chatId}` };
787
+ let menu;
788
+ try {
789
+ menu = await runtime.channelControls.handle(scope, content.trim());
790
+ }
791
+ catch {
792
+ menu = { text: 'Control unavailable or expired. Please open the command again.', buttons: [] };
793
+ }
794
+ await this.sendOrchestrationControl(channelSource, chatId, menu, meta);
795
+ res.writeHead(200);
796
+ res.end('ok');
797
+ return;
798
+ }
799
+ // Keep the legacy /stop path, but never turn disabled controls into model prompts.
800
+ if (!/^\/stop(?:\s|$)/.test(content.trim())) {
801
+ await this.sendOrchestrationControl(channelSource, chatId, { text: 'These controls require orchestration mode.', buttons: [] }, meta);
802
+ res.writeHead(200);
803
+ res.end('ok');
804
+ return;
805
+ }
806
+ }
807
+ if (channelOrchestration && !(0, builtin_commands_1.isBuiltinCommand)(content.trim(), channelSource)) {
808
+ const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, channelSource);
809
+ const orchestration = await this.getOrchestration();
810
+ const { media, quoted, details } = await (0, channel_input_media_1.channelInputMedia)(this.agentConfig, this.agentsBaseDir, channelSource, chatId, meta, AgentRunner.discardEphemeralStaging);
811
+ const senderId = meta.user_id ?? meta.user ?? chatId;
812
+ const accepted = orchestration.submitInput({ scope: { agentId: this.agentConfig.id, agentSessionId: sessionId, source: channelSource,
813
+ 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}` },
814
+ text: content || '[Attachment inspection requested]', attachmentIds: media, trustedChannelMember: true,
815
+ modality: ['voice', 'audio'].includes(meta.media_type ?? meta.attachment_kind) ? 'voice_note' : 'text',
816
+ 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,
817
+ 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 });
818
+ // The durable receipt precedes receiver acknowledgment and inference.
819
+ res.writeHead(200);
820
+ res.end('ok');
821
+ void accepted.response.catch(error => {
822
+ // The durable decision/outbox owns failure delivery and activity, including autonomous turns.
823
+ this.logger.error('Conversation channel turn failed', { chatId, code: error.code ?? 'ORCHESTRATION_ERROR', ...(error.timeout ? { timeout: error.timeout } : {}) });
824
+ });
825
+ return;
826
+ }
827
+ res.writeHead(200);
828
+ res.end('ok');
489
829
  // Slack: remember the current message's thread context so the
490
830
  // auto-forward fallback (writeAutoForward) can stay in-thread. A
491
831
  // top-level message clears it so a later fallback isn't misrouted
@@ -554,6 +894,10 @@ class AgentRunner extends events_1.EventEmitter {
554
894
  return;
555
895
  }
556
896
  catch (err) {
897
+ if (!res.headersSent) {
898
+ res.writeHead(503);
899
+ res.end('Channel input was not accepted');
900
+ }
557
901
  this.logger.warn('Failed to parse channel callback body', {
558
902
  error: err.message,
559
903
  });
@@ -597,6 +941,10 @@ class AgentRunner extends events_1.EventEmitter {
597
941
  return;
598
942
  }
599
943
  const session = this.sessions.get(chatId);
944
+ if (session?.runtimeProfile || this.requiresDurableChannelIngress('telegram')) {
945
+ respond({ ok: false, error: 'Recovery is owned by orchestration; legacy turn replay is disabled.' });
946
+ return;
947
+ }
600
948
  const autoRecover = this.gatewayConfig.gateway.selfHealing?.autoRecover === true;
601
949
  const req = {
602
950
  incidentId: body.incidentId ?? '',
@@ -764,6 +1112,146 @@ class AgentRunner extends events_1.EventEmitter {
764
1112
  return;
765
1113
  }
766
1114
  const command = body.command;
1115
+ if (command === 'telegram_voices') {
1116
+ const chatId = body.chat_id ?? '', payload = body.payload ?? {};
1117
+ if (!/^\d+$/.test(chatId) || payload.user_id !== chatId) {
1118
+ respond({ success: false }, 400);
1119
+ return;
1120
+ }
1121
+ if (!this.agentConfig.orchestration?.enabled || !this.agentConfig.orchestration.channels?.includes('telegram')) {
1122
+ respond({ success: false, error: 'Voice controls unavailable.' });
1123
+ return;
1124
+ }
1125
+ try {
1126
+ const voices = (await this.getOrchestration()).telegramVoices;
1127
+ if (payload.action === 'dismiss' && typeof payload.menu_id === 'string') {
1128
+ voices.dismiss(chatId, payload.menu_id);
1129
+ respond({ success: true });
1130
+ }
1131
+ else if (payload.action === 'choose' && typeof payload.menu_id === 'string' && typeof payload.index === 'number')
1132
+ respond({ success: true, ...await voices.choose(chatId, payload.menu_id, payload.index) });
1133
+ else
1134
+ 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) });
1135
+ }
1136
+ catch {
1137
+ respond({ success: false, error: 'Voice list unavailable or provider changed. Use /voices to refresh.' });
1138
+ }
1139
+ return;
1140
+ }
1141
+ if (command === 'telegram_question') {
1142
+ const chatId = body.chat_id ?? '', payload = body.payload ?? {};
1143
+ if (!/^\d+$/.test(chatId) || payload.user_id !== chatId) {
1144
+ respond({ success: false }, 400);
1145
+ return;
1146
+ }
1147
+ if (!this.agentConfig.orchestration?.enabled || !this.agentConfig.orchestration.channels?.includes('telegram')) {
1148
+ respond({ success: false, error: 'Task controls require orchestration mode.' });
1149
+ return;
1150
+ }
1151
+ if (typeof payload.question_id !== 'string' || !['snooze', 'mute'].includes(String(payload.action))) {
1152
+ respond({ success: false, error: 'Invalid question command.' }, 400);
1153
+ return;
1154
+ }
1155
+ try {
1156
+ const runtime = await this.getOrchestration();
1157
+ const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, 'telegram');
1158
+ const menu = runtime.questionControls.handle({ channel: 'telegram', chatId, thread: typeof payload.thread === 'string' ? payload.thread : '', sessionId, principalId: `telegram:${payload.user_id}` }, `/task_question ${payload.question_id} ${payload.action}`);
1159
+ if (!menu)
1160
+ throw Error('INVALID_QUESTION');
1161
+ respond({ success: true, text: menu.text });
1162
+ }
1163
+ catch {
1164
+ respond({ success: false, error: 'Question unavailable or already answered. Use /tasks to refresh.' });
1165
+ }
1166
+ return;
1167
+ }
1168
+ if (command === 'telegram_tasks') {
1169
+ const chatId = body.chat_id ?? '', payload = body.payload ?? {}, principalId = `telegram:${payload.user_id ?? ''}`;
1170
+ if (!/^\d+$/.test(chatId) || payload.user_id !== chatId) {
1171
+ respond({ success: false }, 400);
1172
+ return;
1173
+ }
1174
+ if (!this.agentConfig.orchestration?.enabled || !this.agentConfig.orchestration.channels?.includes('telegram')) {
1175
+ respond({ success: false, error: 'Task controls require orchestration mode.' });
1176
+ return;
1177
+ }
1178
+ try {
1179
+ const runtime = await this.getOrchestration();
1180
+ const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, 'telegram');
1181
+ if (payload.session_id !== undefined && payload.session_id !== sessionId) {
1182
+ respond({ success: false, error: 'TASK_SESSION_CHANGED' });
1183
+ return;
1184
+ }
1185
+ if (payload.action === 'detail' || payload.action === 'cancel') {
1186
+ if (typeof payload.task_id !== 'string')
1187
+ throw Error('INVALID_TASK');
1188
+ const task = payload.action === 'cancel' ? runtime.taskControls.cancel(sessionId, principalId, payload.task_id) : runtime.taskControls.detail(sessionId, principalId, payload.task_id);
1189
+ respond({ success: true, sessionId, task });
1190
+ }
1191
+ else
1192
+ respond({ success: true, sessionId, ...runtime.taskControls.list(sessionId, principalId, typeof payload.page === 'number' ? payload.page : 0) });
1193
+ }
1194
+ catch {
1195
+ respond({ success: false, error: 'Task unavailable. Use /tasks to refresh.' });
1196
+ }
1197
+ return;
1198
+ }
1199
+ if (command === 'task_stop') {
1200
+ const chatId = body.chat_id ?? '', principalId = `telegram:${body.payload?.user_id ?? ''}`;
1201
+ if (!/^\d+$/.test(chatId) || body.payload?.user_id !== chatId) {
1202
+ respond({ success: false, error: 'invalid_request' }, 400);
1203
+ return;
1204
+ }
1205
+ try {
1206
+ if (!(this.agentConfig.orchestration?.enabled && this.agentConfig.orchestration.channels?.includes('telegram'))) {
1207
+ if (body.payload?.menu_id) {
1208
+ respond({ success: false, error: 'Orchestration is disabled.' });
1209
+ return;
1210
+ }
1211
+ await this.handleCommandStop(chatId);
1212
+ respond({ success: true, legacy: true });
1213
+ return;
1214
+ }
1215
+ const runtime = await this.getOrchestration();
1216
+ const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, 'telegram');
1217
+ if (typeof body.payload?.menu_id === 'string' && typeof body.payload?.index === 'number') {
1218
+ respond({ success: true, ...runtime.stopControls.choose(sessionId, principalId, body.payload.menu_id, body.payload.index) });
1219
+ }
1220
+ else {
1221
+ const menu = runtime.stopControls.open(sessionId, principalId);
1222
+ respond({ success: true, ...menu, text: (0, stop_controls_1.stopMenuText)(menu, true) });
1223
+ }
1224
+ }
1225
+ catch {
1226
+ respond({ success: false, error: 'Selection unavailable. Use /stop to refresh the task list.' });
1227
+ }
1228
+ return;
1229
+ }
1230
+ if (command === 'telegram_voice') {
1231
+ const chatId = body.chat_id ?? '';
1232
+ const value = body.payload?.enabled;
1233
+ const mode = body.payload?.mode;
1234
+ if (!/^\d+$/.test(chatId) || (value !== undefined && typeof value !== 'boolean') || (mode !== undefined && !voice_reply_policy_1.VOICE_REPLY_MODES.includes(mode))) {
1235
+ respond({ success: false, error: 'invalid_request' }, 400);
1236
+ return;
1237
+ }
1238
+ if (!this.agentConfig.orchestration?.enabled || !this.agentConfig.orchestration.channels?.includes('telegram') || !this.agentConfig.voice?.enabled || this.agentConfig.voice?.notes?.replyWithVoice === false) {
1239
+ respond({ success: false, error: 'voice_not_configured' });
1240
+ return;
1241
+ }
1242
+ try {
1243
+ const runtime = await this.getOrchestration();
1244
+ if (mode !== undefined)
1245
+ runtime.store.setTelegramVoiceMode(chatId, mode);
1246
+ else if (typeof value === 'boolean')
1247
+ runtime.store.setTelegramVoice(chatId, value);
1248
+ respond({ success: true, enabled: runtime.store.telegramVoice(chatId), mode: runtime.store.telegramVoiceMode(chatId) });
1249
+ }
1250
+ catch {
1251
+ respond({ success: false, error: 'voice_settings_unavailable' }, 500);
1252
+ }
1253
+ return;
1254
+ }
767
1255
  if (command === 'get_model') {
768
1256
  respond({ model: this.agentConfig.claude.model });
769
1257
  return;
@@ -796,6 +1284,10 @@ class AgentRunner extends events_1.EventEmitter {
796
1284
  // its initData. The browser-facing routes live on the gateway; here we only
797
1285
  // mint the pairing and hand back the phone-openable link.
798
1286
  if (command === 'cli_pair') {
1287
+ if (this.gatewayConfig.gateway.headless !== false || this.agentConfig.type === 'app-agent' || this.agentConfig.orchestration?.enabled) {
1288
+ respond({ success: false, error: 'interactive_mode_required' });
1289
+ return;
1290
+ }
799
1291
  const payload = body.payload ?? {};
800
1292
  const channel = payload['channel'];
801
1293
  const userId = typeof payload['user_id'] === 'string' ? payload['user_id'] : '';
@@ -1240,6 +1732,8 @@ class AgentRunner extends events_1.EventEmitter {
1240
1732
  session.setProcessing(true);
1241
1733
  const turnText = blocks.join('\n');
1242
1734
  // Skill-learning: mark the start of a user turn (cheap, best-effort).
1735
+ const originMeta = entries[0]?.meta ?? {};
1736
+ this.skillNotificationOrigins.set(sessionId, { source: channelSource, chatId, thread: originMeta.thread_ts ?? originMeta.message_thread_id ?? '' });
1243
1737
  this.skillLearning?.onTurnStart(chatId, sessionId, entries[0]?.content ?? '', invokedSkills);
1244
1738
  session.sendMessage(turnText);
1245
1739
  // Remember this turn for the C1 guarded resend (Phase 3b): reset the
@@ -2115,7 +2609,7 @@ class AgentRunner extends events_1.EventEmitter {
2115
2609
  // event. Start the typing-done timer here so the indicator stays alive
2116
2610
  // through multi-turn tool-call sequences and only stops when all work is done.
2117
2611
  if (obj['type'] === 'session_idle' && proc.backend === 'pty-shell') {
2118
- // A parent can become idle immediately after dispatching background
2612
+ // An agent can become idle immediately after dispatching background
2119
2613
  // Agent/Workflow work. Keep the Telegram typing signal and processing
2120
2614
  // sentinel alive until the task notification starts its follow-up turn.
2121
2615
  const waitingForBackgroundWork = proc.retainBackgroundWorkingState();
@@ -2171,7 +2665,7 @@ class AgentRunner extends events_1.EventEmitter {
2171
2665
  }, this.channelFor(mapKey)).catch(() => { });
2172
2666
  }
2173
2667
  proc.on('backgroundWorkExpired', () => {
2174
- // The child task never notified its parent within the bounded grace
2668
+ // The child task never notified its agent within the bounded grace
2175
2669
  // window; release the Telegram typing signal instead of leaving it live.
2176
2670
  this.writeTypingDone(mapKey);
2177
2671
  });
@@ -2236,6 +2730,9 @@ class AgentRunner extends events_1.EventEmitter {
2236
2730
  });
2237
2731
  return proc;
2238
2732
  }
2733
+ apiTaskStopReply(sessionId, principalId, text) {
2734
+ return this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes('api') ? this.orchestration?.stopControls.replyCommand(sessionId, principalId, text) : undefined;
2735
+ }
2239
2736
  static isApiBuiltinCommand(content) {
2240
2737
  return (0, builtin_commands_1.isBuiltinCommand)(content, 'api');
2241
2738
  }
@@ -2435,6 +2932,13 @@ class AgentRunner extends events_1.EventEmitter {
2435
2932
  * "No turn in progress." that fired whenever interrupt() no-op'd mid-spawn.
2436
2933
  */
2437
2934
  async handleCommandStop(chatId) {
2935
+ if (this.orchestration && this.agentConfig.orchestration?.enabled) {
2936
+ const sessionId = await this.sessionStore.getActiveSessionId(this.agentConfig.id, chatId, this.channelFor(chatId));
2937
+ if (this.orchestration.stopResponse(sessionId)) {
2938
+ this.writeAutoForward(chatId, 'Response stopped. Tasks remain active.');
2939
+ return;
2940
+ }
2941
+ }
2438
2942
  // Cancel queued + buffered work first so nothing flushes in behind the interrupt.
2439
2943
  const buffered = this.channelCoalesce.get(chatId);
2440
2944
  if (buffered?.timer)
@@ -2816,7 +3320,9 @@ class AgentRunner extends events_1.EventEmitter {
2816
3320
  const { immediate, deferred } = await this.restartOrDefer({
2817
3321
  skipBusy: false,
2818
3322
  deferIdle: true,
2819
- filter: (proc) => proc.connectorConfigChanged(connectorId, target),
3323
+ // Managed turns use a fresh subprocess/config on their next decision or task.
3324
+ // Never send them through legacy restart/replay while effects may be in flight.
3325
+ filter: (proc) => !proc.runtimeProfile && proc.connectorConfigChanged(connectorId, target),
2820
3326
  });
2821
3327
  return { restarted: immediate + deferred > 0 };
2822
3328
  }
@@ -3043,7 +3549,7 @@ class AgentRunner extends events_1.EventEmitter {
3043
3549
  startIdleCleaner() {
3044
3550
  this.idleCleanerTimer = setInterval(async () => {
3045
3551
  for (const [id, proc] of this.sessions) {
3046
- // A parent that just dispatched Agent/Workflow/Monitor work is CLI-idle
3552
+ // An agent that just dispatched Agent/Workflow/Monitor work is CLI-idle
3047
3553
  // but that dispatch may still need this session to receive its
3048
3554
  // eventual task notification.
3049
3555
  if (proc.hasLikelyOutstandingBackgroundWork())
@@ -3058,16 +3564,20 @@ class AgentRunner extends events_1.EventEmitter {
3058
3564
  }, 5 * 60 * 1000);
3059
3565
  }
3060
3566
  async start() {
3567
+ if (this.agentConfig.orchestration?.enabled || fs.existsSync(path.join(this.agentDir, 'orchestration.db')))
3568
+ await this.getOrchestration();
3061
3569
  this.stopping = false;
3062
3570
  await this.sweepStaleSessionDirs();
3063
3571
  await this.startCallbackServer();
3064
3572
  if (this.agentConfig.telegram?.botToken) {
3065
- this.receiver = new receiver_1.TelegramReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir);
3573
+ this.receiver = new receiver_1.TelegramReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir, this.gatewayConfig.gateway.headless !== false);
3066
3574
  this.receiver.start();
3575
+ this.telegramCommandConfig = this.telegramCommandsKey();
3067
3576
  }
3068
3577
  if (this.agentConfig.discord?.botToken) {
3069
- this.discordReceiver = new receiver_2.DiscordReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir);
3578
+ this.discordReceiver = new receiver_2.DiscordReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir, this.gatewayConfig.gateway.headless !== false);
3070
3579
  this.discordReceiver.start();
3580
+ this.discordCommandConfig = this.telegramCommandsKey();
3071
3581
  }
3072
3582
  // LINE slow-LLM postback button: gateway-side token lifecycle + cache.
3073
3583
  // Enabled for any LINE agent unless its threshold is 0 (then the MCP
@@ -3128,8 +3638,34 @@ class AgentRunner extends events_1.EventEmitter {
3128
3638
  }
3129
3639
  }
3130
3640
  }
3641
+ telegramCommandsKey() {
3642
+ return JSON.stringify([this.gatewayConfig.gateway.headless !== false, this.agentConfig.type,
3643
+ Boolean(this.agentConfig.orchestration?.enabled), this.agentConfig.orchestration?.channels ?? ['api']]);
3644
+ }
3645
+ /** Reload only the receiver when command visibility changes; Agent/Worker work continues. */
3646
+ refreshTelegramCommands() {
3647
+ const discord = this.discordReceiver, discordKey = this.telegramCommandsKey();
3648
+ if (discord && discordKey !== this.discordCommandConfig) {
3649
+ this.discordCommandConfig = discordKey;
3650
+ void discord.stop().then(() => { if (this.discordReceiver !== discord || this.stopping)
3651
+ return; this.discordReceiver = null; this.startDiscordReceiver(); });
3652
+ }
3653
+ const previous = this.receiver, key = this.telegramCommandsKey();
3654
+ if (!previous || key === this.telegramCommandConfig)
3655
+ return;
3656
+ this.telegramCommandConfig = key;
3657
+ void previous.stop().then(() => {
3658
+ if (this.receiver !== previous || this.stopping)
3659
+ return;
3660
+ this.receiver = null;
3661
+ this.startTelegramReceiver();
3662
+ });
3663
+ }
3131
3664
  updateAgentConfig(newConfig) {
3665
+ newConfig = (0, gateway_config_1.applyGatewayOrchestration)(newConfig, this.gatewayConfig);
3666
+ this.orchestration?.updateAgentConfig(newConfig);
3132
3667
  this.agentConfig = newConfig;
3668
+ this.refreshTelegramCommands();
3133
3669
  // Restart LineReplyManager if the LINE config changed so the live instance
3134
3670
  // picks up a new access token, threshold, or labels without a full restart.
3135
3671
  this.stopLineReply();
@@ -3381,6 +3917,25 @@ class AgentRunner extends events_1.EventEmitter {
3381
3917
  });
3382
3918
  }
3383
3919
  }
3920
+ if (update.reply) {
3921
+ if (update.reply.messageId)
3922
+ meta.replied_message_id = update.reply.messageId;
3923
+ if (update.reply.text)
3924
+ meta.replied_text = update.reply.text;
3925
+ const ref = update.reply.image ?? update.reply.file;
3926
+ if (ref)
3927
+ try {
3928
+ const bytes = await (0, ilink_client_1.downloadWeixinImage)(ref, { maxBytes: media_store_1.MediaStore.maxUploadBytes });
3929
+ const name = update.reply.file?.fileName.replace(/[^a-zA-Z0-9_.-]/g, '_') ?? 'image.jpg';
3930
+ const dest = path.join(os.tmpdir(), `wechat-reply-${safeId}-${Date.now()}-${name}`);
3931
+ await fsPromises.writeFile(dest, bytes, { mode: 0o600 });
3932
+ meta.replied_image_path = dest;
3933
+ meta.media_ephemeral = '1';
3934
+ }
3935
+ catch {
3936
+ meta.attachment_error = 'The quoted attachment could not be downloaded. Do not claim to have read it.';
3937
+ }
3938
+ }
3384
3939
  try {
3385
3940
  await fetch(`http://127.0.0.1:${this.callbackPort}/channel`, {
3386
3941
  method: 'POST',
@@ -3458,8 +4013,9 @@ class AgentRunner extends events_1.EventEmitter {
3458
4013
  return;
3459
4014
  if (!this.agentConfig.telegram?.botToken)
3460
4015
  return;
3461
- this.receiver = new receiver_1.TelegramReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir);
4016
+ this.receiver = new receiver_1.TelegramReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir, this.gatewayConfig.gateway.headless !== false);
3462
4017
  this.receiver.start();
4018
+ this.telegramCommandConfig = this.telegramCommandsKey();
3463
4019
  this.logger.info('TelegramReceiver hot-started', { agentId: this.agentConfig.id });
3464
4020
  }
3465
4021
  stopTelegramReceiver() {
@@ -3477,8 +4033,9 @@ class AgentRunner extends events_1.EventEmitter {
3477
4033
  return;
3478
4034
  if (!this.agentConfig.discord?.botToken)
3479
4035
  return;
3480
- this.discordReceiver = new receiver_2.DiscordReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir);
4036
+ this.discordReceiver = new receiver_2.DiscordReceiver(this.agentConfig, this.callbackPort, this.gatewayConfig.gateway.logDir, this.gatewayConfig.gateway.headless !== false);
3481
4037
  this.discordReceiver.start();
4038
+ this.discordCommandConfig = this.telegramCommandsKey();
3482
4039
  this.logger.info('DiscordReceiver hot-started', { agentId: this.agentConfig.id });
3483
4040
  }
3484
4041
  stopDiscordReceiver() {
@@ -3493,6 +4050,12 @@ class AgentRunner extends events_1.EventEmitter {
3493
4050
  }
3494
4051
  async stop() {
3495
4052
  this.stopping = true;
4053
+ if (this.orchestrationStarting) {
4054
+ const orchestration = await this.orchestrationStarting.catch(() => undefined);
4055
+ await orchestration?.close();
4056
+ this.orchestration = undefined;
4057
+ this.orchestrationStarting = undefined;
4058
+ }
3496
4059
  if (this.idleCleanerTimer !== null) {
3497
4060
  clearInterval(this.idleCleanerTimer);
3498
4061
  this.idleCleanerTimer = null;
@@ -3575,6 +4138,17 @@ class AgentRunner extends events_1.EventEmitter {
3575
4138
  isRunning() {
3576
4139
  return this.receiver?.isRunning() ?? false;
3577
4140
  }
4141
+ getOrchestrationSummary() {
4142
+ if (!this.agentConfig.orchestration?.enabled && !this.orchestration)
4143
+ return undefined;
4144
+ 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: [] };
4145
+ const processes = [...this.sessions.values()].filter(p => p.runtimeProfile?.role === 'agent' && p.isRunning());
4146
+ return { ...summary, sessions: summary.sessions.map(s => {
4147
+ const p = processes.find(p => p.sessionId === s.sessionId);
4148
+ return { ...s, model: p?.model ?? s.model, tokens: p?.totalTokens ?? 0, spawnedAt: p?.spawnedAt ?? 0,
4149
+ uptimeSec: p ? Math.floor((Date.now() - p.spawnedAt) / 1000) : 0 };
4150
+ }), agentProcesses: processes.map(p => ({ sessionId: p.sessionId, hostProcessId: p.processId, container: this.agentConfig.container })) };
4151
+ }
3578
4152
  getSessionsSummary() {
3579
4153
  const now = Date.now();
3580
4154
  // A single logical session can appear multiple times in the ring buffer: a
@@ -3612,6 +4186,8 @@ class AgentRunner extends events_1.EventEmitter {
3612
4186
  * - Appends user message and assistant reply to SessionStore for history persistence.
3613
4187
  */
3614
4188
  async sendApiMessage(sessionId, chatId, message, opts) {
4189
+ if (this.orchestrationForApi(sessionId))
4190
+ return this.sendOrchestratedApi(sessionId, chatId, message, opts);
3615
4191
  if (this.pendingApiSessions.has(sessionId)) {
3616
4192
  const err = Object.assign(new Error(`Session ${sessionId} already has a pending request`), { code: 'CONFLICT' });
3617
4193
  throw err;
@@ -3894,6 +4470,18 @@ class AgentRunner extends events_1.EventEmitter {
3894
4470
  * expires (#421).
3895
4471
  */
3896
4472
  async sendApiMessageStream(sessionId, chatId, message, callbacks, opts) {
4473
+ if (this.orchestrationForApi(sessionId)) {
4474
+ if (this.pendingApiSessions.has(sessionId))
4475
+ throw Object.assign(new Error('Session already has a pending request'), { code: 'CONFLICT' });
4476
+ const turn = this.turnStreams.start((0, turn_stream_1.turnStreamKey)('api', sessionId), opts.requestId ?? (0, crypto_1.randomUUID)());
4477
+ const sink = (0, turn_stream_1.callbackSink)(callbacks);
4478
+ turn.attach(sink, 0);
4479
+ void this.sendOrchestratedApi(sessionId, chatId, message, { ...opts, requestId: turn.requestId }, text => turn.emit({ type: 'text_delta', text }), event => { if (event.type === 'tool_use')
4480
+ turn.emit({ ...event, type: 'tool_use' }); })
4481
+ .then(result => this.turnStreams.complete(turn, (0, turn_stream_1.resultEvent)(result.text, result.attachments)))
4482
+ .catch(error => this.turnStreams.complete(turn, (0, turn_stream_1.errorEvent)(error), error));
4483
+ return () => turn.detach(sink);
4484
+ }
3897
4485
  if (this.pendingApiSessions.has(sessionId)) {
3898
4486
  const err = Object.assign(new Error(`Session ${sessionId} already has a pending request`), { code: 'CONFLICT' });
3899
4487
  throw err;
@@ -4365,6 +4953,8 @@ class AgentRunner extends events_1.EventEmitter {
4365
4953
  if (!AgentRunner.isApiBuiltinCommand(cmd)) {
4366
4954
  throw new Error(`Unknown command: ${cmd}`);
4367
4955
  }
4956
+ if (cmd === '/stop' && this.agentConfig.orchestration?.enabled && opts?.principalId)
4957
+ this.orchestration?.authorizeSession(sessionId, opts.principalId);
4368
4958
  // Register session in the api-{chatId} index on first use (same as sendApiMessageStream)
4369
4959
  await this.sessionStore.ensureApiSession(agentId, storeChatId, sessionId).catch((err) => {
4370
4960
  this.logger.warn('Failed to register API session in index for command', { agentId, chatId, sessionId, error: err.message });
@@ -4387,7 +4977,7 @@ class AgentRunner extends events_1.EventEmitter {
4387
4977
  // Persist the full user command before executing so it appears in history.
4388
4978
  // Skip for /clear — clearSession() below wipes the table anyway; only the response survives.
4389
4979
  if (cmd !== '/clear') {
4390
- persist('user', command);
4980
+ persist('user', opts?.displayCommand ?? command);
4391
4981
  }
4392
4982
  let result;
4393
4983
  let responseText;
@@ -4402,17 +4992,41 @@ class AgentRunner extends events_1.EventEmitter {
4402
4992
  : `Current model: ${model}`;
4403
4993
  }
4404
4994
  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;
4995
+ if (this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes('api') && this.orchestration && opts?.principalId) {
4996
+ const selection = /^\/stop\s+([a-f0-9-]{36})\s+(\d+)$/.exec(command.trim());
4997
+ if (selection) {
4998
+ try {
4999
+ const selected = this.orchestration.stopControls.choose(sessionId, opts.principalId, selection[1], Number(selection[2]));
5000
+ result = { ...selected };
5001
+ responseText = selected.text;
5002
+ }
5003
+ catch {
5004
+ result = { error: 'STOP_SELECTION_UNAVAILABLE' };
5005
+ responseText = 'Selection unavailable. Use /stop to refresh the task list.';
5006
+ }
5007
+ }
5008
+ else {
5009
+ const menu = this.orchestration.stopControls.open(sessionId, opts.principalId);
5010
+ responseText = (0, stop_controls_1.stopMenuText)(menu);
5011
+ result = { ...menu, responseText };
5012
+ }
5013
+ forcePersist = true;
5014
+ }
5015
+ else {
5016
+ const session = this.sessions.get(sessionId);
5017
+ const stopped = this.agentConfig.orchestration?.enabled && this.orchestration?.isBusy(sessionId)
5018
+ ? this.orchestration.stopResponse(sessionId)
5019
+ : session ? session.interrupt() : false;
5020
+ result = { stopped };
5021
+ responseText = stopped
5022
+ ? 'Session was interrupted before I could respond.'
5023
+ : 'No active session to stop.';
5024
+ // A turn was actually cut off — always leave a visible note in history so the
5025
+ // web Stop button (skipPersist:true, to suppress the "/stop" command echo) and
5026
+ // a typed /stop command show the same outcome instead of the button leaving a
5027
+ // silently-dangling user turn.
5028
+ forcePersist = stopped;
5029
+ }
4416
5030
  }
4417
5031
  else if (cmd === '/restart') {
4418
5032
  this.restartProcess(sessionId).catch(() => { });
@@ -4692,6 +5306,100 @@ class AgentRunner extends events_1.EventEmitter {
4692
5306
  const mediaPaths = this.historyDb.clearSession(`api-${chatId}`, sessionId);
4693
5307
  media_store_1.MediaStore.deleteMediaFiles(this.agentsBaseDir, this.agentConfig.id, mediaPaths);
4694
5308
  }
5309
+ /** The route has already authorized this API principal for the Agent. Preserve
5310
+ * the original channel binding, but never impersonate its human sender. */
5311
+ async sendOrchestratedChannel(rawChatId, channel, sessionId, message, senderName, callbacks, opts) {
5312
+ if (!opts.principalId)
5313
+ throw new Error('Authenticated principal required for conversation orchestration');
5314
+ const runtime = await this.getOrchestration();
5315
+ const rows = runtime.store.all('SELECT * FROM conversations WHERE agent_session_id=?', sessionId);
5316
+ if (rows.length > 1 || rows.some(row => row.source !== channel || row.chat_id !== rawChatId))
5317
+ throw new Error('Ambiguous or mismatched channel session');
5318
+ if (!rows.length) {
5319
+ const index = await this.sessionStore.loadIndex(this.agentConfig.id, rawChatId, channel);
5320
+ if (!index?.sessions.some(session => session.id === sessionId))
5321
+ throw new Error('Session not found');
5322
+ }
5323
+ const requestId = opts.requestId ?? (0, crypto_1.randomUUID)();
5324
+ const accepted = runtime.submitInput({ scope: { agentId: this.agentConfig.id, agentSessionId: sessionId,
5325
+ source: channel, accountId: String(rows[0]?.account_id ?? this.agentConfig.id), chatId: rawChatId,
5326
+ threadKey: String(rows[0]?.thread_key ?? ''), principalId: opts.principalId },
5327
+ text: message, requestId, trustedChannelMember: true, metadata: { senderName, senderId: opts.principalId } }, { execute: opts.allowTools ?? false, writeMemory: false });
5328
+ const turn = this.turnStreams.start((0, turn_stream_1.turnStreamKey)(channel, sessionId), requestId);
5329
+ const sink = (0, turn_stream_1.callbackSink)(callbacks);
5330
+ turn.attach(sink, 0);
5331
+ const displayed = new Map();
5332
+ const unsubscribe = runtime.subscribeText(sessionId, opts.principalId, event => {
5333
+ if (!runtime.responseBelongsToInput(accepted.inputId, event.responseId))
5334
+ return;
5335
+ const previous = displayed.get(event.responseId) ?? '';
5336
+ if (event.text.startsWith(previous)) {
5337
+ const delta = event.text.slice(previous.length);
5338
+ displayed.set(event.responseId, event.text);
5339
+ if (delta)
5340
+ turn.emit({ type: 'text_delta', text: delta });
5341
+ }
5342
+ });
5343
+ const input = runtime.store.get('SELECT conversation_id FROM conversation_inputs WHERE id=?', accepted.inputId);
5344
+ const conversation = runtime.store.assertMember(String(input.conversation_id), opts.principalId);
5345
+ let cursor = { streamId: String(conversation.stream_id), seq: Number(conversation.last_event_seq) };
5346
+ const events = runtime.events.subscribe(String(conversation.id), opts.principalId, cursor);
5347
+ let closed = false;
5348
+ let timer;
5349
+ const close = () => { closed = true; clearTimeout(timer); unsubscribe(); events.close(); };
5350
+ const forward = (page) => {
5351
+ if (page.snapshotRequired)
5352
+ throw new Error('Tool activity history expired; reload session activity');
5353
+ for (const event of page.events) {
5354
+ if (event.seq <= cursor.seq)
5355
+ continue;
5356
+ const payload = event.payload;
5357
+ if (event.type === 'tool.activity' && payload.type === 'tool_use')
5358
+ turn.emit({ ...payload, type: 'tool_use' });
5359
+ cursor = { streamId: page.cursor.streamId, seq: event.seq };
5360
+ }
5361
+ };
5362
+ const drain = () => {
5363
+ while (true) {
5364
+ const page = runtime.events.read(String(conversation.id), opts.principalId, cursor);
5365
+ forward(page);
5366
+ if (!page.snapshotRequired && !page.events.length)
5367
+ break;
5368
+ }
5369
+ };
5370
+ const fail = (error) => {
5371
+ if (closed)
5372
+ return;
5373
+ try {
5374
+ drain();
5375
+ }
5376
+ catch { /* Preserve the original stream/provider failure. */ }
5377
+ this.turnStreams.completeAndRelease(turn, (0, turn_stream_1.errorEvent)(error), error);
5378
+ close();
5379
+ };
5380
+ void (async () => {
5381
+ for await (const page of events.pages) {
5382
+ if (closed)
5383
+ break;
5384
+ forward(page);
5385
+ }
5386
+ })().catch(fail);
5387
+ timer = setTimeout(() => fail(Object.assign(new Error('Agent response timeout'), { code: 'TIMEOUT_SOFT' })), opts.timeoutMs);
5388
+ void accepted.response.then(text => {
5389
+ if (closed)
5390
+ return;
5391
+ try {
5392
+ // Drain before terminal success, even when completion precedes the next poll.
5393
+ drain();
5394
+ this.turnStreams.completeAndRelease(turn, (0, turn_stream_1.resultEvent)(text, []));
5395
+ close();
5396
+ }
5397
+ catch (error) {
5398
+ fail(error instanceof Error ? error : new Error('Tool activity stream failed'));
5399
+ }
5400
+ }, fail);
5401
+ return () => { turn.detach(sink); }; // Durable work and original channel delivery survive disconnect.
5402
+ }
4695
5403
  /**
4696
5404
  * Send a message into an existing channel session (cross-channel continuation from UI).
4697
5405
  * The session process receives full history context from the session JSON (Layer 1).
@@ -4701,6 +5409,10 @@ class AgentRunner extends events_1.EventEmitter {
4701
5409
  * parser — and writing here as well produced a second row per turn. See done().
4702
5410
  */
4703
5411
  async sendMessageToSession(rawChatId, channel, sessionId, message, senderName, callbacks, opts) {
5412
+ const managed = (this.agentConfig.orchestration?.enabled && (this.agentConfig.orchestration.channels ?? ['api']).includes(channel)) ||
5413
+ (this.orchestration?.ownsSession(sessionId) && !this.orchestration.canReturnToLegacy());
5414
+ if (managed)
5415
+ return this.sendOrchestratedChannel(rawChatId, channel, sessionId, message, senderName, callbacks, opts);
4704
5416
  // Ensure the session process uses the correct channel source
4705
5417
  this.channelSourceMap.set(rawChatId, channel);
4706
5418
  // Channel sessions use agent-level model (not per-session)