botmux 3.13.0 → 3.14.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 (508) hide show
  1. package/README.en.md +1 -1
  2. package/README.md +25 -1
  3. package/dist/.runtime-build-id +1 -1
  4. package/dist/adapters/adopt-route.d.ts +58 -0
  5. package/dist/adapters/adopt-route.d.ts.map +1 -1
  6. package/dist/adapters/adopt-route.js +136 -0
  7. package/dist/adapters/adopt-route.js.map +1 -1
  8. package/dist/adapters/backend/capabilities.js +1 -1
  9. package/dist/adapters/backend/capabilities.js.map +1 -1
  10. package/dist/adapters/backend/herdr-backend.d.ts +1 -1
  11. package/dist/adapters/backend/herdr-backend.d.ts.map +1 -1
  12. package/dist/adapters/backend/herdr-backend.js +2 -1
  13. package/dist/adapters/backend/herdr-backend.js.map +1 -1
  14. package/dist/adapters/backend/riff-backend.d.ts +97 -0
  15. package/dist/adapters/backend/riff-backend.d.ts.map +1 -1
  16. package/dist/adapters/backend/riff-backend.js +380 -66
  17. package/dist/adapters/backend/riff-backend.js.map +1 -1
  18. package/dist/adapters/backend/sandbox.d.ts +8 -0
  19. package/dist/adapters/backend/sandbox.d.ts.map +1 -1
  20. package/dist/adapters/backend/sandbox.js +16 -2
  21. package/dist/adapters/backend/sandbox.js.map +1 -1
  22. package/dist/adapters/backend/tmux-backend.d.ts +12 -13
  23. package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
  24. package/dist/adapters/backend/tmux-backend.js +113 -53
  25. package/dist/adapters/backend/tmux-backend.js.map +1 -1
  26. package/dist/adapters/backend/tmux-pipe-backend.d.ts +4 -3
  27. package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -1
  28. package/dist/adapters/backend/tmux-pipe-backend.js +12 -9
  29. package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
  30. package/dist/adapters/backend/zellij-backend.d.ts +2 -3
  31. package/dist/adapters/backend/zellij-backend.d.ts.map +1 -1
  32. package/dist/adapters/backend/zellij-backend.js +6 -13
  33. package/dist/adapters/backend/zellij-backend.js.map +1 -1
  34. package/dist/adapters/backend/zellij-observe-backend.d.ts +2 -1
  35. package/dist/adapters/backend/zellij-observe-backend.d.ts.map +1 -1
  36. package/dist/adapters/backend/zellij-observe-backend.js +6 -3
  37. package/dist/adapters/backend/zellij-observe-backend.js.map +1 -1
  38. package/dist/adapters/backend/zmx-backend.d.ts.map +1 -1
  39. package/dist/adapters/backend/zmx-backend.js +40 -18
  40. package/dist/adapters/backend/zmx-backend.js.map +1 -1
  41. package/dist/adapters/cli/claude-code.d.ts.map +1 -1
  42. package/dist/adapters/cli/claude-code.js +12 -1
  43. package/dist/adapters/cli/claude-code.js.map +1 -1
  44. package/dist/adapters/cli/codex.d.ts.map +1 -1
  45. package/dist/adapters/cli/codex.js +3 -2
  46. package/dist/adapters/cli/codex.js.map +1 -1
  47. package/dist/adapters/cli/dsh.d.ts +4 -0
  48. package/dist/adapters/cli/dsh.d.ts.map +1 -0
  49. package/dist/adapters/cli/dsh.js +96 -0
  50. package/dist/adapters/cli/dsh.js.map +1 -0
  51. package/dist/adapters/cli/fs-policy.d.ts.map +1 -1
  52. package/dist/adapters/cli/fs-policy.js +5 -0
  53. package/dist/adapters/cli/fs-policy.js.map +1 -1
  54. package/dist/adapters/cli/opencode.d.ts +52 -1
  55. package/dist/adapters/cli/opencode.d.ts.map +1 -1
  56. package/dist/adapters/cli/opencode.js +117 -76
  57. package/dist/adapters/cli/opencode.js.map +1 -1
  58. package/dist/adapters/cli/opencode2.d.ts +4 -0
  59. package/dist/adapters/cli/opencode2.d.ts.map +1 -0
  60. package/dist/adapters/cli/opencode2.js +137 -0
  61. package/dist/adapters/cli/opencode2.js.map +1 -0
  62. package/dist/adapters/cli/read-isolation.d.ts +1 -1
  63. package/dist/adapters/cli/read-isolation.d.ts.map +1 -1
  64. package/dist/adapters/cli/read-isolation.js +5 -2
  65. package/dist/adapters/cli/read-isolation.js.map +1 -1
  66. package/dist/adapters/cli/registry.d.ts +7 -1
  67. package/dist/adapters/cli/registry.d.ts.map +1 -1
  68. package/dist/adapters/cli/registry.js +19 -6
  69. package/dist/adapters/cli/registry.js.map +1 -1
  70. package/dist/adapters/cli/shared-hints.d.ts.map +1 -1
  71. package/dist/adapters/cli/shared-hints.js +13 -0
  72. package/dist/adapters/cli/shared-hints.js.map +1 -1
  73. package/dist/adapters/cli/types.d.ts +24 -3
  74. package/dist/adapters/cli/types.d.ts.map +1 -1
  75. package/dist/adapters/hook-command.d.ts +8 -0
  76. package/dist/adapters/hook-command.d.ts.map +1 -1
  77. package/dist/adapters/hook-command.js +11 -0
  78. package/dist/adapters/hook-command.js.map +1 -1
  79. package/dist/adapters/hook-installer.d.ts +16 -1
  80. package/dist/adapters/hook-installer.d.ts.map +1 -1
  81. package/dist/adapters/hook-installer.js +232 -2
  82. package/dist/adapters/hook-installer.js.map +1 -1
  83. package/dist/bot-registry.d.ts +116 -8
  84. package/dist/bot-registry.d.ts.map +1 -1
  85. package/dist/bot-registry.js +52 -4
  86. package/dist/bot-registry.js.map +1 -1
  87. package/dist/cli/pm2-start-transaction.d.ts +14 -0
  88. package/dist/cli/pm2-start-transaction.d.ts.map +1 -1
  89. package/dist/cli/pm2-start-transaction.js +25 -9
  90. package/dist/cli/pm2-start-transaction.js.map +1 -1
  91. package/dist/cli/restart-failure-notification.d.ts +54 -0
  92. package/dist/cli/restart-failure-notification.d.ts.map +1 -0
  93. package/dist/cli/restart-failure-notification.js +142 -0
  94. package/dist/cli/restart-failure-notification.js.map +1 -0
  95. package/dist/cli/restart-failure-owner.d.ts +20 -0
  96. package/dist/cli/restart-failure-owner.d.ts.map +1 -0
  97. package/dist/cli/restart-failure-owner.js +27 -0
  98. package/dist/cli/restart-failure-owner.js.map +1 -0
  99. package/dist/cli/restart-shutdown-preflight.d.ts +24 -0
  100. package/dist/cli/restart-shutdown-preflight.d.ts.map +1 -0
  101. package/dist/cli/restart-shutdown-preflight.js +105 -0
  102. package/dist/cli/restart-shutdown-preflight.js.map +1 -0
  103. package/dist/cli/send-dispatch.d.ts +26 -0
  104. package/dist/cli/send-dispatch.d.ts.map +1 -1
  105. package/dist/cli/send-dispatch.js +30 -0
  106. package/dist/cli/send-dispatch.js.map +1 -1
  107. package/dist/cli.d.ts +4 -1
  108. package/dist/cli.d.ts.map +1 -1
  109. package/dist/cli.js +665 -399
  110. package/dist/cli.js.map +1 -1
  111. package/dist/codex-app-runner.js +38 -15
  112. package/dist/codex-app-runner.js.map +1 -1
  113. package/dist/codex-rpc-lifecycle.d.ts +14 -2
  114. package/dist/codex-rpc-lifecycle.d.ts.map +1 -1
  115. package/dist/codex-rpc-lifecycle.js +13 -3
  116. package/dist/codex-rpc-lifecycle.js.map +1 -1
  117. package/dist/core/ask-broker.d.ts +3 -0
  118. package/dist/core/ask-broker.d.ts.map +1 -1
  119. package/dist/core/ask-broker.js +29 -7
  120. package/dist/core/ask-broker.js.map +1 -1
  121. package/dist/core/ask-hook/registry.d.ts.map +1 -1
  122. package/dist/core/ask-hook/registry.js +4 -0
  123. package/dist/core/ask-hook/registry.js.map +1 -1
  124. package/dist/core/ask-types.d.ts +5 -1
  125. package/dist/core/ask-types.d.ts.map +1 -1
  126. package/dist/core/ask-types.js.map +1 -1
  127. package/dist/core/browser-restart.d.ts +112 -0
  128. package/dist/core/browser-restart.d.ts.map +1 -0
  129. package/dist/core/browser-restart.js +233 -0
  130. package/dist/core/browser-restart.js.map +1 -0
  131. package/dist/core/command-handler.d.ts +19 -11
  132. package/dist/core/command-handler.d.ts.map +1 -1
  133. package/dist/core/command-handler.js +237 -43
  134. package/dist/core/command-handler.js.map +1 -1
  135. package/dist/core/current-turn-provenance.d.ts.map +1 -1
  136. package/dist/core/current-turn-provenance.js +7 -27
  137. package/dist/core/current-turn-provenance.js.map +1 -1
  138. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  139. package/dist/core/dashboard-ipc-server.js +317 -10
  140. package/dist/core/dashboard-ipc-server.js.map +1 -1
  141. package/dist/core/dispatch-registry.d.ts.map +1 -1
  142. package/dist/core/dispatch-registry.js +6 -0
  143. package/dist/core/dispatch-registry.js.map +1 -1
  144. package/dist/core/dispatch-report-binding.d.ts +31 -0
  145. package/dist/core/dispatch-report-binding.d.ts.map +1 -0
  146. package/dist/core/dispatch-report-binding.js +88 -0
  147. package/dist/core/dispatch-report-binding.js.map +1 -0
  148. package/dist/core/doc-comment-prompt.d.ts +2 -0
  149. package/dist/core/doc-comment-prompt.d.ts.map +1 -1
  150. package/dist/core/doc-comment-prompt.js +6 -0
  151. package/dist/core/doc-comment-prompt.js.map +1 -1
  152. package/dist/core/host-overload-alert.d.ts +25 -1
  153. package/dist/core/host-overload-alert.d.ts.map +1 -1
  154. package/dist/core/host-overload-alert.js +69 -8
  155. package/dist/core/host-overload-alert.js.map +1 -1
  156. package/dist/core/local-terminal-opener.js +1 -1
  157. package/dist/core/local-terminal-opener.js.map +1 -1
  158. package/dist/core/managed-origin-capability.d.ts +7 -6
  159. package/dist/core/managed-origin-capability.d.ts.map +1 -1
  160. package/dist/core/managed-origin-capability.js +32 -1
  161. package/dist/core/managed-origin-capability.js.map +1 -1
  162. package/dist/core/passthrough-commands.d.ts.map +1 -1
  163. package/dist/core/passthrough-commands.js +3 -2
  164. package/dist/core/passthrough-commands.js.map +1 -1
  165. package/dist/core/plugins/materializer.d.ts.map +1 -1
  166. package/dist/core/plugins/materializer.js +1 -0
  167. package/dist/core/plugins/materializer.js.map +1 -1
  168. package/dist/core/producer-quiescence.d.ts +70 -0
  169. package/dist/core/producer-quiescence.d.ts.map +1 -0
  170. package/dist/core/producer-quiescence.js +90 -0
  171. package/dist/core/producer-quiescence.js.map +1 -0
  172. package/dist/core/raw-command-write-options.d.ts +10 -0
  173. package/dist/core/raw-command-write-options.d.ts.map +1 -0
  174. package/dist/core/raw-command-write-options.js +23 -0
  175. package/dist/core/raw-command-write-options.js.map +1 -0
  176. package/dist/core/raw-command-writer.d.ts +4 -0
  177. package/dist/core/raw-command-writer.d.ts.map +1 -1
  178. package/dist/core/raw-command-writer.js +15 -6
  179. package/dist/core/raw-command-writer.js.map +1 -1
  180. package/dist/core/reply-target.d.ts +11 -0
  181. package/dist/core/reply-target.d.ts.map +1 -1
  182. package/dist/core/reply-target.js +22 -7
  183. package/dist/core/reply-target.js.map +1 -1
  184. package/dist/core/report-session-relay.d.ts +53 -0
  185. package/dist/core/report-session-relay.d.ts.map +1 -0
  186. package/dist/core/report-session-relay.js +116 -0
  187. package/dist/core/report-session-relay.js.map +1 -0
  188. package/dist/core/session-discovery.d.ts +5 -0
  189. package/dist/core/session-discovery.d.ts.map +1 -1
  190. package/dist/core/session-discovery.js +13 -0
  191. package/dist/core/session-discovery.js.map +1 -1
  192. package/dist/core/session-group-birth.d.ts +7 -0
  193. package/dist/core/session-group-birth.d.ts.map +1 -0
  194. package/dist/core/session-group-birth.js +145 -0
  195. package/dist/core/session-group-birth.js.map +1 -0
  196. package/dist/core/session-manager.d.ts +16 -21
  197. package/dist/core/session-manager.d.ts.map +1 -1
  198. package/dist/core/session-manager.js +140 -21
  199. package/dist/core/session-manager.js.map +1 -1
  200. package/dist/core/trigger-session.d.ts.map +1 -1
  201. package/dist/core/trigger-session.js +18 -3
  202. package/dist/core/trigger-session.js.map +1 -1
  203. package/dist/core/types.d.ts +18 -0
  204. package/dist/core/types.d.ts.map +1 -1
  205. package/dist/core/types.js.map +1 -1
  206. package/dist/core/worker-pool.d.ts +63 -11
  207. package/dist/core/worker-pool.d.ts.map +1 -1
  208. package/dist/core/worker-pool.js +241 -13
  209. package/dist/core/worker-pool.js.map +1 -1
  210. package/dist/daemon.d.ts +33 -0
  211. package/dist/daemon.d.ts.map +1 -1
  212. package/dist/daemon.js +900 -137
  213. package/dist/daemon.js.map +1 -1
  214. package/dist/dashboard/auth.d.ts +20 -0
  215. package/dist/dashboard/auth.d.ts.map +1 -1
  216. package/dist/dashboard/auth.js +86 -1
  217. package/dist/dashboard/auth.js.map +1 -1
  218. package/dist/dashboard/bot-payload.d.ts +5 -0
  219. package/dist/dashboard/bot-payload.d.ts.map +1 -1
  220. package/dist/dashboard/bot-payload.js +4 -1
  221. package/dist/dashboard/bot-payload.js.map +1 -1
  222. package/dist/dashboard/federation-spoke-api.d.ts.map +1 -1
  223. package/dist/dashboard/federation-spoke-api.js +27 -3
  224. package/dist/dashboard/federation-spoke-api.js.map +1 -1
  225. package/dist/dashboard/feedback-analytics-api.d.ts +6 -0
  226. package/dist/dashboard/feedback-analytics-api.d.ts.map +1 -0
  227. package/dist/dashboard/feedback-analytics-api.js +39 -0
  228. package/dist/dashboard/feedback-analytics-api.js.map +1 -0
  229. package/dist/dashboard/session-action-timeout.d.ts +3 -0
  230. package/dist/dashboard/session-action-timeout.d.ts.map +1 -0
  231. package/dist/dashboard/session-action-timeout.js +12 -0
  232. package/dist/dashboard/session-action-timeout.js.map +1 -0
  233. package/dist/dashboard/vc-consumer-profiles-api.d.ts +9 -3
  234. package/dist/dashboard/vc-consumer-profiles-api.d.ts.map +1 -1
  235. package/dist/dashboard/vc-consumer-profiles-api.js +2 -1
  236. package/dist/dashboard/vc-consumer-profiles-api.js.map +1 -1
  237. package/dist/dashboard/web/app.d.ts.map +1 -1
  238. package/dist/dashboard/web/app.js +48 -5
  239. package/dist/dashboard/web/app.js.map +1 -1
  240. package/dist/dashboard/web/bot-defaults-page.d.ts +4 -0
  241. package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -1
  242. package/dist/dashboard/web/bot-defaults-page.js +273 -36
  243. package/dist/dashboard/web/bot-defaults-page.js.map +1 -1
  244. package/dist/dashboard/web/bot-defaults.d.ts +6 -0
  245. package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
  246. package/dist/dashboard/web/bot-defaults.js.map +1 -1
  247. package/dist/dashboard/web/dashboard-routes.d.ts.map +1 -1
  248. package/dist/dashboard/web/dashboard-routes.js +1 -0
  249. package/dist/dashboard/web/dashboard-routes.js.map +1 -1
  250. package/dist/dashboard/web/feedback-page.d.ts +3 -0
  251. package/dist/dashboard/web/feedback-page.d.ts.map +1 -0
  252. package/dist/dashboard/web/feedback-page.js +34 -0
  253. package/dist/dashboard/web/feedback-page.js.map +1 -0
  254. package/dist/dashboard/web/groups-page.d.ts.map +1 -1
  255. package/dist/dashboard/web/groups-page.js +7 -2
  256. package/dist/dashboard/web/groups-page.js.map +1 -1
  257. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  258. package/dist/dashboard/web/i18n.js +114 -22
  259. package/dist/dashboard/web/i18n.js.map +1 -1
  260. package/dist/dashboard/web/team-federation-page.d.ts.map +1 -1
  261. package/dist/dashboard/web/team-federation-page.js +26 -2
  262. package/dist/dashboard/web/team-federation-page.js.map +1 -1
  263. package/dist/dashboard/web/team-federation.d.ts +19 -0
  264. package/dist/dashboard/web/team-federation.d.ts.map +1 -1
  265. package/dist/dashboard/web/team-federation.js +3 -0
  266. package/dist/dashboard/web/team-federation.js.map +1 -1
  267. package/dist/dashboard/web/update-action.d.ts +8 -0
  268. package/dist/dashboard/web/update-action.d.ts.map +1 -1
  269. package/dist/dashboard/web/update-action.js +10 -0
  270. package/dist/dashboard/web/update-action.js.map +1 -1
  271. package/dist/dashboard/web/vc-consumer-profiles-section.d.ts.map +1 -1
  272. package/dist/dashboard/web/vc-consumer-profiles-section.js +58 -9
  273. package/dist/dashboard/web/vc-consumer-profiles-section.js.map +1 -1
  274. package/dist/dashboard-web/app.js +1 -1
  275. package/dist/dashboard-web/chunks/bot-defaults-page-4RENHE7J.js +10 -0
  276. package/dist/dashboard-web/chunks/chunk-2WQDS65D.js +1 -0
  277. package/dist/dashboard-web/chunks/{chunk-CU4SUKK3.js → chunk-6O7KXFD2.js} +1 -1
  278. package/dist/dashboard-web/chunks/chunk-G5XGBCM6.js +1 -0
  279. package/dist/dashboard-web/chunks/{chunk-6JTZJRQJ.js → chunk-Q2MT4VWH.js} +15 -15
  280. package/dist/dashboard-web/chunks/{chunk-5QJB24I2.js → chunk-U3ZFSO7E.js} +1 -1
  281. package/dist/dashboard-web/chunks/connectors-page-2KZO5KSJ.js +1 -0
  282. package/dist/dashboard-web/chunks/feedback-page-CLVTOLLO.js +1 -0
  283. package/dist/dashboard-web/chunks/groups-page-HLASRTSY.js +8 -0
  284. package/dist/dashboard-web/chunks/{insights-page-EVGGJI5L.js → insights-page-4RPU2CRU.js} +1 -1
  285. package/dist/dashboard-web/chunks/{monitor-room-HRGR2ENP.js → monitor-room-OTKQ2CXK.js} +1 -1
  286. package/dist/dashboard-web/chunks/monitoring-page-IJPUB6JT.js +2 -0
  287. package/dist/dashboard-web/chunks/office-page-QSR3J44Y.js +1 -0
  288. package/dist/dashboard-web/chunks/overview-page-6OK63V4V.js +1 -0
  289. package/dist/dashboard-web/chunks/roles-page-6NWJMOB5.js +1 -0
  290. package/dist/dashboard-web/chunks/schedules-page-6I3LNXD4.js +1 -0
  291. package/dist/dashboard-web/chunks/{sessions-page-DXBCDWCS.js → sessions-page-B2TXGJDO.js} +1 -1
  292. package/dist/dashboard-web/chunks/settings-page-6KJADSC6.js +2 -0
  293. package/dist/dashboard-web/chunks/{skills-page-2QAVB2SA.js → skills-page-M2Q6L5CB.js} +1 -1
  294. package/dist/dashboard-web/chunks/team-federation-page-7I7FDE3B.js +1 -0
  295. package/dist/dashboard-web/chunks/{v3-page-AY35LKCX.js → v3-page-HQXRL65L.js} +1 -1
  296. package/dist/dashboard-web/chunks/whiteboards-page-LQKC4TAZ.js +1 -0
  297. package/dist/dashboard-web/style.css +35 -0
  298. package/dist/dashboard.js +199 -12
  299. package/dist/dashboard.js.map +1 -1
  300. package/dist/desktop/shared/shell-path-probes.d.ts +1 -1
  301. package/dist/desktop/shared/shell-path-probes.js +2 -2
  302. package/dist/desktop/shared/shell-path-probes.js.map +1 -1
  303. package/dist/dsh-runner.d.ts +3 -0
  304. package/dist/dsh-runner.d.ts.map +1 -0
  305. package/dist/dsh-runner.js +666 -0
  306. package/dist/dsh-runner.js.map +1 -0
  307. package/dist/global-config.d.ts +24 -0
  308. package/dist/global-config.d.ts.map +1 -1
  309. package/dist/global-config.js +35 -0
  310. package/dist/global-config.js.map +1 -1
  311. package/dist/i18n/en.d.ts.map +1 -1
  312. package/dist/i18n/en.js +25 -6
  313. package/dist/i18n/en.js.map +1 -1
  314. package/dist/i18n/zh.d.ts.map +1 -1
  315. package/dist/i18n/zh.js +25 -6
  316. package/dist/i18n/zh.js.map +1 -1
  317. package/dist/im/lark/ask-card.d.ts +3 -1
  318. package/dist/im/lark/ask-card.d.ts.map +1 -1
  319. package/dist/im/lark/ask-card.js +61 -5
  320. package/dist/im/lark/ask-card.js.map +1 -1
  321. package/dist/im/lark/card-builder.d.ts.map +1 -1
  322. package/dist/im/lark/card-builder.js +13 -6
  323. package/dist/im/lark/card-builder.js.map +1 -1
  324. package/dist/im/lark/card-handler.d.ts +2 -1
  325. package/dist/im/lark/card-handler.d.ts.map +1 -1
  326. package/dist/im/lark/card-handler.js +145 -5
  327. package/dist/im/lark/card-handler.js.map +1 -1
  328. package/dist/im/lark/event-dispatcher.d.ts +18 -0
  329. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  330. package/dist/im/lark/event-dispatcher.js +36 -9
  331. package/dist/im/lark/event-dispatcher.js.map +1 -1
  332. package/dist/im/lark/md-card.d.ts +18 -0
  333. package/dist/im/lark/md-card.d.ts.map +1 -1
  334. package/dist/im/lark/md-card.js +21 -0
  335. package/dist/im/lark/md-card.js.map +1 -1
  336. package/dist/im/lark/relay-target-routing.d.ts.map +1 -1
  337. package/dist/im/lark/relay-target-routing.js +3 -2
  338. package/dist/im/lark/relay-target-routing.js.map +1 -1
  339. package/dist/im/lark/reply-mode-command.d.ts.map +1 -1
  340. package/dist/im/lark/reply-mode-command.js +19 -8
  341. package/dist/im/lark/reply-mode-command.js.map +1 -1
  342. package/dist/im/lark/skill-feedback-card.d.ts +15 -0
  343. package/dist/im/lark/skill-feedback-card.d.ts.map +1 -0
  344. package/dist/im/lark/skill-feedback-card.js +149 -0
  345. package/dist/im/lark/skill-feedback-card.js.map +1 -0
  346. package/dist/services/bot-config-store.d.ts +10 -1
  347. package/dist/services/bot-config-store.d.ts.map +1 -1
  348. package/dist/services/bot-config-store.js +103 -3
  349. package/dist/services/bot-config-store.js.map +1 -1
  350. package/dist/services/bridge-fallback-gate.d.ts +14 -0
  351. package/dist/services/bridge-fallback-gate.d.ts.map +1 -1
  352. package/dist/services/bridge-fallback-gate.js +15 -0
  353. package/dist/services/bridge-fallback-gate.js.map +1 -1
  354. package/dist/services/codex-reasoning-effort.d.ts +9 -0
  355. package/dist/services/codex-reasoning-effort.d.ts.map +1 -0
  356. package/dist/services/codex-reasoning-effort.js +23 -0
  357. package/dist/services/codex-reasoning-effort.js.map +1 -0
  358. package/dist/services/codex-service-tier.d.ts +1 -1
  359. package/dist/services/codex-service-tier.d.ts.map +1 -1
  360. package/dist/services/codex-transcript.d.ts +9 -0
  361. package/dist/services/codex-transcript.d.ts.map +1 -1
  362. package/dist/services/codex-transcript.js +13 -6
  363. package/dist/services/codex-transcript.js.map +1 -1
  364. package/dist/services/feed-group-tagger.d.ts +15 -0
  365. package/dist/services/feed-group-tagger.d.ts.map +1 -0
  366. package/dist/services/feed-group-tagger.js +348 -0
  367. package/dist/services/feed-group-tagger.js.map +1 -0
  368. package/dist/services/feedback-analytics.d.ts +82 -0
  369. package/dist/services/feedback-analytics.d.ts.map +1 -0
  370. package/dist/services/feedback-analytics.js +95 -0
  371. package/dist/services/feedback-analytics.js.map +1 -0
  372. package/dist/services/feedback-outbox.d.ts +24 -0
  373. package/dist/services/feedback-outbox.d.ts.map +1 -0
  374. package/dist/services/feedback-outbox.js +12 -0
  375. package/dist/services/feedback-outbox.js.map +1 -0
  376. package/dist/services/feedback-policy-resolver.d.ts +56 -0
  377. package/dist/services/feedback-policy-resolver.d.ts.map +1 -0
  378. package/dist/services/feedback-policy-resolver.js +156 -0
  379. package/dist/services/feedback-policy-resolver.js.map +1 -0
  380. package/dist/services/feedback-policy.d.ts +41 -0
  381. package/dist/services/feedback-policy.d.ts.map +1 -0
  382. package/dist/services/feedback-policy.js +102 -0
  383. package/dist/services/feedback-policy.js.map +1 -0
  384. package/dist/services/feedback-webhook-dispatcher.d.ts +76 -0
  385. package/dist/services/feedback-webhook-dispatcher.d.ts.map +1 -0
  386. package/dist/services/feedback-webhook-dispatcher.js +237 -0
  387. package/dist/services/feedback-webhook-dispatcher.js.map +1 -0
  388. package/dist/services/grant-prefs-store.d.ts +3 -0
  389. package/dist/services/grant-prefs-store.d.ts.map +1 -1
  390. package/dist/services/grant-prefs-store.js +30 -1
  391. package/dist/services/grant-prefs-store.js.map +1 -1
  392. package/dist/services/groups-store.d.ts +14 -0
  393. package/dist/services/groups-store.d.ts.map +1 -1
  394. package/dist/services/groups-store.js +23 -0
  395. package/dist/services/groups-store.js.map +1 -1
  396. package/dist/services/local-cli-opener.d.ts +1 -1
  397. package/dist/services/local-cli-opener.d.ts.map +1 -1
  398. package/dist/services/local-cli-opener.js +2 -0
  399. package/dist/services/local-cli-opener.js.map +1 -1
  400. package/dist/services/prompt-context-store.d.ts +29 -0
  401. package/dist/services/prompt-context-store.d.ts.map +1 -0
  402. package/dist/services/prompt-context-store.js +205 -0
  403. package/dist/services/prompt-context-store.js.map +1 -0
  404. package/dist/services/session-group-avatar.d.ts +6 -0
  405. package/dist/services/session-group-avatar.d.ts.map +1 -0
  406. package/dist/services/session-group-avatar.js +96 -0
  407. package/dist/services/session-group-avatar.js.map +1 -0
  408. package/dist/services/session-group-title.d.ts +47 -0
  409. package/dist/services/session-group-title.d.ts.map +1 -0
  410. package/dist/services/session-group-title.js +226 -0
  411. package/dist/services/session-group-title.js.map +1 -0
  412. package/dist/services/session-groups-store.d.ts +30 -0
  413. package/dist/services/session-groups-store.d.ts.map +1 -0
  414. package/dist/services/session-groups-store.js +132 -0
  415. package/dist/services/session-groups-store.js.map +1 -0
  416. package/dist/services/session-store.d.ts +58 -0
  417. package/dist/services/session-store.d.ts.map +1 -1
  418. package/dist/services/session-store.js +174 -0
  419. package/dist/services/session-store.js.map +1 -1
  420. package/dist/services/skill-feedback-store.d.ts +252 -0
  421. package/dist/services/skill-feedback-store.d.ts.map +1 -0
  422. package/dist/services/skill-feedback-store.js +795 -0
  423. package/dist/services/skill-feedback-store.js.map +1 -0
  424. package/dist/services/skill-registry-store.d.ts +23 -0
  425. package/dist/services/skill-registry-store.d.ts.map +1 -1
  426. package/dist/services/skill-registry-store.js +93 -7
  427. package/dist/services/skill-registry-store.js.map +1 -1
  428. package/dist/services/team-store.d.ts +4 -0
  429. package/dist/services/team-store.d.ts.map +1 -1
  430. package/dist/services/team-store.js +16 -0
  431. package/dist/services/team-store.js.map +1 -1
  432. package/dist/services/traex-transcript.d.ts +32 -4
  433. package/dist/services/traex-transcript.d.ts.map +1 -1
  434. package/dist/services/traex-transcript.js +116 -8
  435. package/dist/services/traex-transcript.js.map +1 -1
  436. package/dist/services/trigger-types.d.ts +1 -1
  437. package/dist/services/trigger-types.d.ts.map +1 -1
  438. package/dist/services/trigger-types.js +2 -2
  439. package/dist/services/trigger-types.js.map +1 -1
  440. package/dist/services/turn-completion-events.d.ts +57 -0
  441. package/dist/services/turn-completion-events.d.ts.map +1 -0
  442. package/dist/services/turn-completion-events.js +179 -0
  443. package/dist/services/turn-completion-events.js.map +1 -0
  444. package/dist/services/vc-meeting-consumer-isolation.d.ts +21 -12
  445. package/dist/services/vc-meeting-consumer-isolation.d.ts.map +1 -1
  446. package/dist/services/vc-meeting-consumer-isolation.js +25 -19
  447. package/dist/services/vc-meeting-consumer-isolation.js.map +1 -1
  448. package/dist/services/vc-meeting-consumer-profile-bootstrap.d.ts +8 -2
  449. package/dist/services/vc-meeting-consumer-profile-bootstrap.d.ts.map +1 -1
  450. package/dist/services/vc-meeting-consumer-profile-bootstrap.js +18 -10
  451. package/dist/services/vc-meeting-consumer-profile-bootstrap.js.map +1 -1
  452. package/dist/setup/bot-config-editor.d.ts.map +1 -1
  453. package/dist/setup/bot-config-editor.js +6 -0
  454. package/dist/setup/bot-config-editor.js.map +1 -1
  455. package/dist/setup/cli-availability.d.ts +2 -1
  456. package/dist/setup/cli-availability.d.ts.map +1 -1
  457. package/dist/setup/cli-availability.js +2 -1
  458. package/dist/setup/cli-availability.js.map +1 -1
  459. package/dist/setup/cli-selection.d.ts +8 -0
  460. package/dist/setup/cli-selection.d.ts.map +1 -1
  461. package/dist/setup/cli-selection.js +31 -0
  462. package/dist/setup/cli-selection.js.map +1 -1
  463. package/dist/setup/ensure-fonts.d.ts +5 -0
  464. package/dist/setup/ensure-fonts.d.ts.map +1 -1
  465. package/dist/setup/ensure-fonts.js +26 -1
  466. package/dist/setup/ensure-fonts.js.map +1 -1
  467. package/dist/setup/lark-scopes.json +3 -1
  468. package/dist/setup/open-platform-automation.d.ts +1 -1
  469. package/dist/setup/open-platform-automation.d.ts.map +1 -1
  470. package/dist/setup/open-platform-automation.js +15 -3
  471. package/dist/setup/open-platform-automation.js.map +1 -1
  472. package/dist/skills/definitions.d.ts +1 -1
  473. package/dist/skills/definitions.d.ts.map +1 -1
  474. package/dist/skills/definitions.js +9 -2
  475. package/dist/skills/definitions.js.map +1 -1
  476. package/dist/types.d.ts +10 -4
  477. package/dist/types.d.ts.map +1 -1
  478. package/dist/utils/user-token.d.ts +38 -0
  479. package/dist/utils/user-token.d.ts.map +1 -1
  480. package/dist/utils/user-token.js +76 -1
  481. package/dist/utils/user-token.js.map +1 -1
  482. package/dist/utils/web-terminal-scroll.d.ts +27 -0
  483. package/dist/utils/web-terminal-scroll.d.ts.map +1 -0
  484. package/dist/utils/web-terminal-scroll.js +60 -0
  485. package/dist/utils/web-terminal-scroll.js.map +1 -0
  486. package/dist/vc-agent/polling-source.d.ts +70 -0
  487. package/dist/vc-agent/polling-source.d.ts.map +1 -1
  488. package/dist/vc-agent/polling-source.js +115 -0
  489. package/dist/vc-agent/polling-source.js.map +1 -1
  490. package/dist/worker.js +237 -76
  491. package/dist/worker.js.map +1 -1
  492. package/dist/workflows/definition.d.ts +22 -22
  493. package/dist/workflows/events/payloads.d.ts +8 -8
  494. package/dist/workflows/events/schema.d.ts +20 -20
  495. package/dist/workflows/migration/v2-run-archive-schema.d.ts +20 -20
  496. package/package.json +1 -1
  497. package/dist/dashboard-web/chunks/bot-defaults-page-NQJFJ45Z.js +0 -10
  498. package/dist/dashboard-web/chunks/chunk-KEEYQ3WF.js +0 -1
  499. package/dist/dashboard-web/chunks/connectors-page-HADHZXXZ.js +0 -1
  500. package/dist/dashboard-web/chunks/groups-page-5TYF46RS.js +0 -8
  501. package/dist/dashboard-web/chunks/monitoring-page-LBU7TTTW.js +0 -2
  502. package/dist/dashboard-web/chunks/office-page-DF6RRBMN.js +0 -1
  503. package/dist/dashboard-web/chunks/overview-page-ZB6QH2EX.js +0 -1
  504. package/dist/dashboard-web/chunks/roles-page-ZBZGHIIF.js +0 -1
  505. package/dist/dashboard-web/chunks/schedules-page-NQVQC3QY.js +0 -1
  506. package/dist/dashboard-web/chunks/settings-page-E7NN4NVQ.js +0 -2
  507. package/dist/dashboard-web/chunks/team-federation-page-VGWL53AE.js +0 -1
  508. package/dist/dashboard-web/chunks/whiteboards-page-BNWUU4KG.js +0 -1
@@ -1,4 +1,6 @@
1
1
  import { readFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
2
4
  import { execFileSync } from 'node:child_process';
3
5
  import { createHash } from 'node:crypto';
4
6
  import { logger } from '../../utils/logger.js';
@@ -193,6 +195,237 @@ export async function cancelRiffTaskById(cfg, taskId) {
193
195
  export function hashUrlForLog(u) {
194
196
  return createHash('sha256').update(u).digest('hex').slice(0, 8);
195
197
  }
198
+ /** The keychain leaf under a ByteCloud tool's storage root:
199
+ * `<root>/bytecloud-auth/keychain/auth/cn/default`, whose JSON holds the
200
+ * `bytecloud_jwt` field. `cn` is ByteCloud CN (riff is an internal CN
201
+ * service). NOTE the sibling `bytecloud-auth/auth/cn/credentials.json` (no
202
+ * `keychain/` segment) carries only metadata (app_id / expires_at / user) and
203
+ * NO `bytecloud_jwt` — we deliberately never read it. */
204
+ const BYTECLOUD_KEYCHAIN_LEAF = join('bytecloud-auth', 'keychain', 'auth', 'cn', 'default');
205
+ /**
206
+ * Reproduce bytedcli's `sanitizeFilenamePart` + AIME base-dir assembly EXACTLY
207
+ * (from `@bytedance-dev/bytedcli` dist/bytedcli-core.js, verified against
208
+ * 0.124.0): a username path segment keeps only `[a-zA-Z0-9._-]` (every other
209
+ * char → `_`), then a lone `.` → `_` and a lone `..` → `__`. Must match
210
+ * byte-for-byte or the AIME keychain path we build won't line up with where
211
+ * bytedcli actually wrote the token.
212
+ */
213
+ function sanitizeAimeUser(user) {
214
+ return user.replace(/[^a-zA-Z0-9._-]/g, '_').replace(/^\.$/, '_').replace(/^\.\.$/, '__');
215
+ }
216
+ /**
217
+ * bytedcli's data-home base when running inside an AIME workspace. bytedcli
218
+ * uses it (in place of `os.homedir()`) ONLY when both `AIME_WORKSPACE_PATH` and
219
+ * `AIME_CURRENT_USER` are set (trimmed non-empty); it then stores under
220
+ * `<workspace>/<sanitizedUser>/.local/share/bytedcli/data/…`. We return the
221
+ * `<workspace>/<sanitizedUser>/.local/share` prefix (parallel to the plain
222
+ * `~/.local/share` data-home, so the shared `join(base,'bytedcli','data')`
223
+ * below lands on the right leaf), or null when this is not an AIME runtime.
224
+ */
225
+ function aimeDataHome(env) {
226
+ const workspace = env.AIME_WORKSPACE_PATH?.trim();
227
+ const user = env.AIME_CURRENT_USER?.trim();
228
+ if (!workspace || !user)
229
+ return null;
230
+ return join(workspace, sanitizeAimeUser(user), '.local', 'share');
231
+ }
232
+ /**
233
+ * The keychain candidates for a ByteCloud tool's `bytecloud-auth/` store, across
234
+ * the CLIs botmux users log into (kaboo-cli / aiden-cli / cjadk / bytedcli).
235
+ *
236
+ * ⚠️ This is NOT a "cast a wide net" list. The selector in
237
+ * `readBytecloudKeychainJwt` picks the globally-freshest token by `exp`
238
+ * REGARDLESS of order, so an extra candidate is not free: a stale/foreign token
239
+ * at a location the tool never actually writes could WIN and shadow the real
240
+ * one. Every entry must be a location the tool genuinely uses on THIS host:
241
+ * - Config-style CLIs (kaboo-cli / aiden-cli / cjadk) resolve their base via
242
+ * Go's os.UserConfigDir (verified against kaboo 1.3.77): macOS →
243
+ * `~/Library/Application Support`, Windows → `%AppData%` (Go errors, does
244
+ * NOT default to `~/AppData/Roaming`, when it is unset — so we emit no
245
+ * config candidate then), otherwise → `$XDG_CONFIG_HOME` (else `~/.config`).
246
+ * We list ONLY the current platform's root, never several — a
247
+ * foreign-platform root is never live here and would only invite shadowing.
248
+ * - cjadk also uses a home dot-dir `~/.cjadk`; aipaas uses `~/.aipaas`.
249
+ * - bytedcli stores under `~/.local/share/bytedcli/data` on Linux, macOS AND
250
+ * Windows: its `bytedcliBaseDir()` (bytedcli-core.js, 0.125.0) has no
251
+ * platform branch and ignores `$XDG_DATA_HOME`. Inside an AIME workspace it
252
+ * swaps the home base for `$AIME_WORKSPACE_PATH/<sanitized $AIME_CURRENT_USER>`
253
+ * — see the fail-closed early return below.
254
+ * Order is otherwise NOT significant (selection is by `exp`, not position).
255
+ * Non-existent candidates simply fail the read and are skipped.
256
+ */
257
+ export function bytecloudKeychainCandidates(home = homedir(), env = process.env, platform = process.platform) {
258
+ const dedupe = (xs) => [...new Set(xs.filter(Boolean))];
259
+ const isMac = platform === 'darwin';
260
+ // --- Full AIME runtime: fail-closed to the AIME identity domain ---------
261
+ // When BOTH AIME vars are set, bytedcli swaps its storage root to
262
+ // `$AIME_WORKSPACE_PATH/<sanitized user>/.local/share/bytedcli/data` and,
263
+ // crucially, does NOT fall back to the host HOME (bytedcliBaseDir returns the
264
+ // AIME root and stops). `os.homedir()` here is still the HOST home — that is
265
+ // precisely WHY bytedcli needs the override — so EVERY host-HOME-derived
266
+ // keychain (the config-style CLIs under ~/.config or Application Support,
267
+ // ~/.cjadk, ~/.aipaas) belongs to a DIFFERENT identity. Reading any of them
268
+ // would cross AIME user identities, and the exp-aware selector below would
269
+ // happily prefer a longer-lived host token. The safe boundary is the
270
+ // identity domain, not the tool name: in a full AIME runtime the ONLY
271
+ // in-domain source is the AIME-scoped bytedcli store. If it holds no live
272
+ // token we return nothing here and the caller fails closed (the user logs in
273
+ // inside AIME) rather than silently authenticating as someone else.
274
+ const aimeHome = aimeDataHome(env);
275
+ if (aimeHome) {
276
+ return [join(aimeHome, 'bytedcli', 'data', BYTECLOUD_KEYCHAIN_LEAF)];
277
+ }
278
+ // --- Ordinary (non-AIME) runtime ---------------------------------------
279
+ // Config-style CLIs (kaboo-cli / aiden-cli / cjadk) resolve their base via
280
+ // Go's os.UserConfigDir (verified against kaboo 1.3.77's embedded ByteCloud
281
+ // auth). That maps per-platform: macOS → `~/Library/Application Support`;
282
+ // Windows → `%AppData%`; everything else → `$XDG_CONFIG_HOME` (falling back
283
+ // to `~/.config`). A single process only ever uses ONE of these — the current
284
+ // platform's. We must key off the actual platform, NOT list several: the
285
+ // exp-aware selector picks the globally-freshest token regardless of order,
286
+ // so a stale token under another platform's root could otherwise shadow the
287
+ // authoritative one (a foreign-platform root is never a live location on this
288
+ // host anyway). `platform` is injectable so every spelling stays testable.
289
+ //
290
+ // `configHome` is null when we cannot name the platform's real config root:
291
+ // on Windows Go ERRORS if `%AppData%` is unset (it does NOT default to
292
+ // `~/AppData/Roaming`), so with APPDATA absent we emit NO config-style
293
+ // candidate rather than invent a phantom path a stale token could shadow
294
+ // from. The other verified candidates (bytedcli, dot-dirs) are unaffected.
295
+ const xdgConfig = env.XDG_CONFIG_HOME?.trim();
296
+ let configHome;
297
+ if (isMac) {
298
+ configHome = join(home, 'Library', 'Application Support');
299
+ }
300
+ else if (platform === 'win32') {
301
+ configHome = env.APPDATA?.trim() || null;
302
+ }
303
+ else {
304
+ configHome = xdgConfig || join(home, '.config');
305
+ }
306
+ // bytedcli keeps `bytedcli/data/bytecloud-auth/...` under its data home.
307
+ // `bytedcliBaseDir()` in `@bytedance-dev/bytedcli` (dist/bytedcli-core.js,
308
+ // 0.125.0) has NO platform branch: in the ordinary case it unconditionally
309
+ // uses `~/.local/share/bytedcli` on Linux, macOS AND Windows, and it ignores
310
+ // $XDG_DATA_HOME. So the single `~/.local/share` data home is correct on
311
+ // every platform — there is no Application Support / %AppData% spelling to
312
+ // add. (The AIME workspace override is the only base swap, handled above.)
313
+ const bytedcliHome = join(home, '.local', 'share');
314
+ const roots = [];
315
+ // Config-dir CLIs (single platform-correct base, when we can name one).
316
+ if (configHome) {
317
+ for (const cli of ['kaboo-cli', 'aiden-cli', 'cjadk'])
318
+ roots.push(join(configHome, cli));
319
+ }
320
+ // Home dot-dir layouts (Linux-observed; harmless as extra candidates elsewhere).
321
+ roots.push(join(home, '.cjadk'));
322
+ roots.push(join(home, '.aipaas'));
323
+ // Data-dir CLI (bytedcli) — the extra `data/` segment is part of its layout.
324
+ roots.push(join(bytedcliHome, 'bytedcli', 'data'));
325
+ return dedupe(roots).map((root) => join(root, BYTECLOUD_KEYCHAIN_LEAF));
326
+ }
327
+ /**
328
+ * Decode a JWT's `exp` (seconds since epoch) from its payload without verifying
329
+ * the signature — we only need the expiry to prefer a live token over a stale
330
+ * one. Returns null for anything we cannot confidently parse as an expiry so it
331
+ * ranks below any parseable-live token (opaque/non-JWT strings, malformed
332
+ * base64, missing/non-number `exp`).
333
+ *
334
+ * A JWS compact JWT is EXACTLY three non-empty base64url segments
335
+ * (`header.payload.signature`) whose header and payload are JSON. We require
336
+ * that shape up front: a 2- or 4-segment string, a segment that isn't
337
+ * base64url (incl. the signature), or a header/payload that isn't a JSON
338
+ * object is NOT a JWT and must never be ranked as a live token where its
339
+ * (accidentally decodable) `exp` could shadow a genuine JWT. We do NOT verify
340
+ * the signature (that is riff's job) — only that the structure is a real JWT.
341
+ */
342
+ const BASE64URL_RE = /^[A-Za-z0-9_-]+$/;
343
+ function decodeJoseJson(seg) {
344
+ try {
345
+ const obj = JSON.parse(Buffer.from(seg, 'base64url').toString('utf-8'));
346
+ // A JOSE header / JWT payload is a JSON object (not an array, not a scalar).
347
+ if (typeof obj !== 'object' || obj === null || Array.isArray(obj))
348
+ return null;
349
+ return obj;
350
+ }
351
+ catch {
352
+ return null;
353
+ }
354
+ }
355
+ export function decodeJwtExp(jwt) {
356
+ const parts = jwt.split('.');
357
+ // Exactly three non-empty, strictly-base64url segments (header.payload.sig).
358
+ if (parts.length !== 3)
359
+ return null;
360
+ if (!parts[0] || !parts[1] || !parts[2])
361
+ return null;
362
+ if (parts.some((p) => !BASE64URL_RE.test(p)))
363
+ return null;
364
+ // Header must decode to a JSON object (confirms it's really JOSE, not just
365
+ // base64url-shaped noise); we don't require a specific `typ`/`alg`.
366
+ if (!decodeJoseJson(parts[0]))
367
+ return null;
368
+ const payload = decodeJoseJson(parts[1]);
369
+ if (!payload)
370
+ return null;
371
+ const exp = payload['exp'];
372
+ return typeof exp === 'number' && Number.isFinite(exp) ? exp : null;
373
+ }
374
+ /**
375
+ * Read the ByteCloud JWT from the keychain candidates, preferring a live token.
376
+ * Pure + injectable (home/env/now) so it is unit-testable without touching the
377
+ * real HOME. Never throws — unreadable/malformed candidates are skipped.
378
+ *
379
+ * Selection (fixes the stale-token-shadows-valid-token hazard: an expired token
380
+ * from an earlier-listed tool must not mask a valid token from a later one):
381
+ * 1. Collect every candidate's non-empty `bytecloud_jwt`, in candidate order.
382
+ * 2. Drop tokens whose decoded `exp` is already past `now`.
383
+ * 3. Among the survivors, pick the one with the greatest `exp` (freshest);
384
+ * candidates whose `exp` we cannot parse (opaque values) rank BELOW any
385
+ * parseable live token and are used only as a last-resort fallback when no
386
+ * parseable-live token exists — so a broken/opaque old value can never
387
+ * shadow a clearly-valid newer token.
388
+ * Returns null when nothing yields a usable token.
389
+ */
390
+ /**
391
+ * Treat a token that expires within this many seconds as already expired. riff
392
+ * task creation reads the JWT once and does a single fetch; a 401 there throws
393
+ * and fails the whole turn (SSE reconnect only covers an ALREADY-created task),
394
+ * and a fresh sandbox cold-boot costs minutes — so a token about to expire
395
+ * mid-request is worse than skipping to a longer-lived candidate. Also absorbs
396
+ * small client/server clock skew.
397
+ */
398
+ export const JWT_EXPIRY_SAFETY_WINDOW_SEC = 30;
399
+ export function readBytecloudKeychainJwt(home = homedir(), env = process.env, nowMs = Date.now(), platform = process.platform) {
400
+ const cutoffSec = nowMs / 1000 + JWT_EXPIRY_SAFETY_WINDOW_SEC;
401
+ let bestLive = null; // parseable, unexpired, freshest
402
+ let opaqueFallback = null; // first exp-less, non-expired-unknown token
403
+ for (const path of bytecloudKeychainCandidates(home, env, platform)) {
404
+ let jwt;
405
+ try {
406
+ const data = JSON.parse(readFileSync(path, 'utf-8'));
407
+ const v = data['bytecloud_jwt'];
408
+ if (typeof v !== 'string' || v.length === 0)
409
+ continue;
410
+ jwt = v;
411
+ }
412
+ catch {
413
+ continue;
414
+ }
415
+ const exp = decodeJwtExp(jwt);
416
+ if (exp === null) {
417
+ // Cannot parse expiry — keep only the first as a last-resort fallback.
418
+ if (opaqueFallback === null)
419
+ opaqueFallback = jwt;
420
+ continue;
421
+ }
422
+ if (exp <= cutoffSec)
423
+ continue; // expired or about to expire — never select.
424
+ if (!bestLive || exp > bestLive.exp)
425
+ bestLive = { jwt, exp };
426
+ }
427
+ return bestLive?.jwt ?? opaqueFallback;
428
+ }
196
429
  function defaultRunGit(cwd) {
197
430
  return (args) => {
198
431
  try {
@@ -236,6 +469,11 @@ function isBareCodexNoiseLine(text) {
236
469
  return false;
237
470
  }
238
471
  }
472
+ /** Terminal riff task statuses (riff openApiDocs task contract). Once a task
473
+ * reaches one of these it will emit no further progress — an `init` replay or
474
+ * a `done` event carrying one of these IS the task's completion. Non-terminal:
475
+ * pending / creating_session / running. */
476
+ const TERMINAL_RIFF_STATUSES = new Set(['completed', 'failed', 'cancelled', 'timeout']);
239
477
  /**
240
478
  * RiffBackend — bridges botmux's SessionBackend interface to riff's HTTP API.
241
479
  *
@@ -274,11 +512,41 @@ export class RiffBackend {
274
512
  * cleared past 64 entries (a session rarely exceeds a few dozen turns). */
275
513
  completedTaskIds = new Set();
276
514
  reconnectAttempts = 0;
277
- maxReconnectAttempts = 3;
515
+ maxReconnectAttempts = 6;
516
+ /** Wall-clock ms when the CURRENT SSE connection was established, or `null`
517
+ * when none is open / the last fetch never connected. Typed `number | null`
518
+ * (not a 0 sentinel) on purpose: 0 is both "not connected" AND a valid number
519
+ * you could subtract, so a future edit dropping the guard would compute a
520
+ * bogus multi-decade lifetime from `Date.now() - 0` and refund forever. `null`
521
+ * makes "never connected" un-subtractable and forces the guard at the type
522
+ * level. The reconnect budget is refunded only when a broken connection had
523
+ * LIVED long enough to be a healthy long connection merely severed by the
524
+ * upstream proxy's fixed ~183s lifetime cap — NOT merely because a connection
525
+ * opened. Keying on "connection lived ≥ reconnectHealthyConnMs" (not on
526
+ * receiving init, and not on any data event — both were falsified/
527
+ * insufficient) is what separates the two cases that look identical from the
528
+ * client:
529
+ * • healthy cap: connection lives ~183s, EOFs → refund → task streams on
530
+ * • dead/hot-loop: connection opens then EOFs within ~1s, repeatedly →
531
+ * NO refund → budget exhausts and bails. Covers BOTH a fetch that never
532
+ * connects (stays null) AND a "connect→init→instant-EOF" loop against a
533
+ * stale-running orphan (lives <threshold) — the latter is exactly the
534
+ * infinite-retry hole a naive "reset on connect/init" would reopen. */
535
+ connectionStartedAtMs = null;
278
536
  /** 预算层级(单调覆盖,见 destroySession 注释);字段化以便测试注入边界。 */
279
537
  cancelTimeoutMs = 4_000;
280
538
  createTimeoutMs = 10_000;
281
539
  destroyDeadlineMs = 20_000;
540
+ /** SSE 重连退避基数(指数退避的第一档);字段化以便测试把重连间隔压到 0。 */
541
+ reconnectBaseDelayMs = 1_000;
542
+ reconnectMaxDelayMs = 30_000;
543
+ /** A broken SSE connection that lived at least this long is treated as a
544
+ * healthy long connection severed by the ~183s proxy cap → refund the
545
+ * reconnect budget. Shorter-lived breaks (dead endpoint / instant-EOF hot
546
+ * loop) do NOT refund. 30s: the cap is metronomic at ~181-183s (6× margin)
547
+ * while pathological EOFs are sub-second, so the two separate cleanly.
548
+ * Field-ized for test injection. */
549
+ reconnectHealthyConnMs = 30_000;
282
550
  /** Exact late/current task whose close cancellation failed. Retained across
283
551
  * the prepare-close handshake so the daemon can persist a retry handle. */
284
552
  closeFailureTaskId = null;
@@ -339,7 +607,7 @@ export class RiffBackend {
339
607
  const fromEnv = process.env[envKey];
340
608
  if (fromEnv)
341
609
  return fromEnv;
342
- // Fallback: try ByteCloud Auth SDK keychain (kaboo-cli / aiden-cli / cjadk)
610
+ // Fallback: try ByteCloud Auth SDK keychain (kaboo-cli / aiden-cli / cjadk / bytedcli)
343
611
  const fromKeychain = this.readJwtFromBytecloudKeychain();
344
612
  if (fromKeychain) {
345
613
  logger.info(`[riff] JWT loaded from ByteCloud keychain`);
@@ -349,23 +617,7 @@ export class RiffBackend {
349
617
  return null;
350
618
  }
351
619
  readJwtFromBytecloudKeychain() {
352
- const home = process.env.HOME ?? '~';
353
- const candidates = [
354
- `${home}/.config/kaboo-cli/bytecloud-auth/keychain/auth/cn/default`,
355
- `${home}/.config/aiden-cli/bytecloud-auth/keychain/auth/cn/default`,
356
- `${home}/.cjadk/bytecloud-auth/keychain/auth/cn/default`,
357
- ];
358
- for (const path of candidates) {
359
- try {
360
- const raw = readFileSync(path, 'utf-8');
361
- const data = JSON.parse(raw);
362
- const jwt = data['bytecloud_jwt'];
363
- if (jwt)
364
- return jwt;
365
- }
366
- catch { /* try next */ }
367
- }
368
- return null;
620
+ return readBytecloudKeychainJwt();
369
621
  }
370
622
  spawn(_bin, _args, _opts) {
371
623
  logger.info(`[riff] spawn (ignoring bin/args, using config: ${this.config.baseUrl})`);
@@ -970,11 +1222,14 @@ export class RiffBackend {
970
1222
  return env;
971
1223
  }
972
1224
  async uploadAndCreate(url, payload, attachments) {
1225
+ // Assemble the request body FIRST (attachment reads can be slow on large
1226
+ // files / slow disks), THEN resolve the JWT immediately before fetch. The
1227
+ // keychain selector skips tokens expiring within a safety window, but that
1228
+ // guarantee only holds if we read the token close to the request — reading
1229
+ // it before a multi-second upload prep could hand off a token that expires
1230
+ // mid-flight. createTimeout only bounds the fetch, not the prep before it.
973
1231
  const headers = {};
974
- const jwt = this.getJwt();
975
- if (jwt)
976
- headers['x-jwt-token'] = jwt;
977
- let resp;
1232
+ let body;
978
1233
  if (attachments.length > 0) {
979
1234
  const form = new FormData();
980
1235
  form.append('payload', JSON.stringify(payload));
@@ -987,12 +1242,18 @@ export class RiffBackend {
987
1242
  logger.warn(`[riff] failed to read attachment ${att.path}: ${err}`);
988
1243
  }
989
1244
  }
990
- resp = await fetch(url, { method: 'POST', headers, body: form, signal: AbortSignal.timeout(this.createTimeoutMs) });
1245
+ body = form;
991
1246
  }
992
1247
  else {
993
1248
  headers['Content-Type'] = 'application/json';
994
- resp = await fetch(url, { method: 'POST', headers, body: JSON.stringify(payload), signal: AbortSignal.timeout(this.createTimeoutMs) });
1249
+ body = JSON.stringify(payload);
995
1250
  }
1251
+ // Resolve JWT last — right before the request — so the safety-window
1252
+ // freshness check reflects the token that actually goes on the wire.
1253
+ const jwt = this.getJwt();
1254
+ if (jwt)
1255
+ headers['x-jwt-token'] = jwt;
1256
+ const resp = await fetch(url, { method: 'POST', headers, body, signal: AbortSignal.timeout(this.createTimeoutMs) });
996
1257
  if (!resp.ok)
997
1258
  throw new Error(`HTTP ${resp.status}: ${await resp.text()}`);
998
1259
  const result = (await resp.json());
@@ -1104,14 +1365,25 @@ export class RiffBackend {
1104
1365
  if (jwt)
1105
1366
  headers['x-jwt-token'] = jwt;
1106
1367
  this.abortController = new AbortController();
1368
+ // Per-connection lifetime clock (see field doc): null until this connection
1369
+ // is confirmed established below. Reset PER streamTask invocation so a fetch
1370
+ // that never connects can't inherit the previous connection's start time.
1371
+ this.connectionStartedAtMs = null;
1107
1372
  try {
1108
1373
  const resp = await fetch(url, { headers, signal: this.abortController.signal });
1109
1374
  if (!resp.ok || !resp.body) {
1110
1375
  throw new Error(`SSE HTTP ${resp.status}`);
1111
1376
  }
1112
- // NOTE: reconnectAttempts is reset per TASK (createTask/followUp), not
1113
- // here resetting on every 200 would let a "connect OK → immediate
1114
- // clean EOF" loop retry forever.
1377
+ // Connection established start its lifetime clock. On break, catch
1378
+ // compares elapsed against reconnectHealthyConnMs to decide whether this
1379
+ // was a healthy ~183s-capped connection (refund budget) or a short-lived
1380
+ // dead/hot-loop break (do not refund).
1381
+ this.connectionStartedAtMs = Date.now();
1382
+ // NOTE: reconnectAttempts is reset per TASK (createTask/followUp) AND
1383
+ // whenever a broken connection had LIVED ≥ reconnectHealthyConnMs (see the
1384
+ // catch) — NOT unconditionally on every 200, which would let a "connect OK
1385
+ // → instant EOF" loop retry forever (the hole the per-task-only reset
1386
+ // originally guarded, which a naive "reset on connect/init" reopens).
1115
1387
  const reader = resp.body.getReader();
1116
1388
  const decoder = new TextDecoder();
1117
1389
  let buffer = '';
@@ -1154,10 +1426,29 @@ export class RiffBackend {
1154
1426
  if (taskId !== this.currentTaskId || this.completedTaskIds.has(taskId))
1155
1427
  return;
1156
1428
  logger.warn(`[riff] SSE stream error: ${err}`);
1429
+ // Core fix: an upstream proxy caps each task-stream connection at a fixed
1430
+ // ~183s lifetime and closes it with a clean EOF (no done event) — verified
1431
+ // against live data: tasks that "重连失败" had actually COMPLETED server-
1432
+ // side; botmux gave up ~22s early on one, 16min early on another. A healthy
1433
+ // long runner task thus breaks every ~183s. If this just-broken connection
1434
+ // had LIVED long enough (≥ reconnectHealthyConnMs), it was such a healthy
1435
+ // capped connection — refund the reconnect budget so those periodic caps
1436
+ // never accumulate into a false failure, letting the task stream until it
1437
+ // truly finishes. A short-lived break (dead endpoint that never connected,
1438
+ // or a connect→instant-EOF hot loop against a stale-running orphan) does
1439
+ // NOT refund, so it still exhausts the budget and bails (no infinite
1440
+ // retry). Keyed on connection LIFETIME — not on connect/init receipt,
1441
+ // which would refund every attempt and reopen the infinite-retry hole.
1442
+ const connLivedMs = this.connectionStartedAtMs !== null ? Date.now() - this.connectionStartedAtMs : 0;
1443
+ if (connLivedMs >= this.reconnectHealthyConnMs)
1444
+ this.reconnectAttempts = 0;
1157
1445
  // Attempt reconnect if task is still running
1158
1446
  if (!this.killed && !this.taskDone && this.reconnectAttempts < this.maxReconnectAttempts) {
1159
1447
  this.reconnectAttempts++;
1160
- const delay = 1000 * this.reconnectAttempts;
1448
+ // Exponential backoff with a cap: 1s,2s,4s,8s,16s,30s(cap). Linear 1s/2s/3s
1449
+ // was negligible against a ~180s connection lifetime anyway; the cap keeps
1450
+ // a truly-unreachable gateway from stalling teardown for minutes.
1451
+ const delay = Math.min(this.reconnectMaxDelayMs, this.reconnectBaseDelayMs * 2 ** (this.reconnectAttempts - 1));
1161
1452
  logger.info(`[riff] SSE reconnect attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts} in ${delay}ms`);
1162
1453
  this.emitLine(`[riff] 连接中断,正在重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})`, 'warn');
1163
1454
  await new Promise((r) => setTimeout(r, delay));
@@ -1172,6 +1463,50 @@ export class RiffBackend {
1172
1463
  }
1173
1464
  }
1174
1465
  }
1466
+ /**
1467
+ * Fire a task's completion exactly once — the turn boundary + final-output
1468
+ * fetch. Called from BOTH the `done` SSE event AND an `init` replay carrying a
1469
+ * terminal status (the task finished while a prior connection was dead and its
1470
+ * `done` was lost with the closed stream). Idempotency & staleness — per TASK,
1471
+ * not per backend: streams can deliver done more than once (observed ~500ms
1472
+ * apart live), and by the time a duplicate (or a reconnect's init replay)
1473
+ * arrives, a queued follow-up may already be running as the NEXT task (write()
1474
+ * reset the global taskDone). A plain boolean guard would re-fire the boundary
1475
+ * mid-way through that next task and falsely mark it done, so gate on:
1476
+ * 1) the completion must belong to the CURRENT task (stale streams no-op)
1477
+ * 2) each task fires the boundary at most once (completedTaskIds)
1478
+ */
1479
+ completeTask(taskId, status, exitCode) {
1480
+ if (taskId !== this.currentTaskId)
1481
+ return;
1482
+ if (this.completedTaskIds.has(taskId))
1483
+ return;
1484
+ this.completedTaskIds.add(taskId);
1485
+ // Bounded FIFO eviction — never a blanket clear(), which would drop the id
1486
+ // just added and let its ~500ms duplicate done re-fire.
1487
+ while (this.completedTaskIds.size > 64) {
1488
+ const oldest = this.completedTaskIds.values().next().value;
1489
+ if (oldest === taskId)
1490
+ break;
1491
+ this.completedTaskIds.delete(oldest);
1492
+ }
1493
+ this.taskDone = true;
1494
+ if (this.config.injectStatusLines !== false) {
1495
+ this.emitLine(`[riff] 任务完成${status ? ` (${status}${exitCode != null ? `, exit=${exitCode}` : ''})` : ''}`, status === 'failed' ? 'warn' : 'ok');
1496
+ }
1497
+ // Fetch final output from task-detail API (SSE has no output events for
1498
+ // runner tasks) BEFORE firing the turn boundary: the boundary flushes queued
1499
+ // follow-ups → currentTaskId flips to the next task → the stale guard would
1500
+ // (correctly) drop THIS task's only report.
1501
+ if (status === 'completed' || status === 'failed') {
1502
+ void this.fetchAndEmitOutput(taskId)
1503
+ .catch(() => { })
1504
+ .finally(() => { this.taskDoneCb?.(); });
1505
+ }
1506
+ else {
1507
+ this.taskDoneCb?.();
1508
+ }
1509
+ }
1175
1510
  handleSseEvent(block, taskId) {
1176
1511
  // Task isolation: once a newer task is current, EVERY event from an older
1177
1512
  // task's stream (output/log/init/session_info/done alike) is inert — a
@@ -1232,48 +1567,27 @@ export class RiffBackend {
1232
1567
  if (changed && !this.accessUrlIsDirect) {
1233
1568
  void this.fetchDirectAccessUrl(taskId);
1234
1569
  }
1570
+ // init REPLAYS the full accumulated task state, including a terminal
1571
+ // `status` when the task finished while our previous connection was
1572
+ // dead (the ~183s cap closes mid-flight and the `done` event is lost
1573
+ // with it). Consume that replay: a terminal status here IS the missed
1574
+ // completion — route it through the same completion path so the turn
1575
+ // ends cleanly instead of the budget eventually exhausting into a
1576
+ // false "重连失败". Non-terminal (running/pending/…) just means the
1577
+ // reconnect resumed a still-live task — no completion, keep streaming.
1578
+ if (eventType === 'init') {
1579
+ const initStatus = data['status'];
1580
+ if (initStatus && TERMINAL_RIFF_STATUSES.has(initStatus)) {
1581
+ const exitCode = data['exitCode'];
1582
+ this.completeTask(taskId, initStatus, exitCode);
1583
+ }
1584
+ }
1235
1585
  break;
1236
1586
  }
1237
1587
  case 'done': {
1238
- // Idempotency & staleness — per TASK, not per backend: streams can
1239
- // deliver done more than once (observed ~500ms apart live), and by
1240
- // the time the duplicate arrives a queued follow-up may already be
1241
- // running as the NEXT task (write() reset the global taskDone). A
1242
- // plain boolean guard would re-fire the turn-boundary callback mid-
1243
- // way through that next task and falsely mark it done, so gate on:
1244
- // 1) the done must belong to the CURRENT task (stale streams no-op)
1245
- // 2) each task fires the boundary at most once (completedTaskIds)
1246
- if (taskId !== this.currentTaskId)
1247
- break;
1248
- if (this.completedTaskIds.has(taskId))
1249
- break;
1250
- this.completedTaskIds.add(taskId);
1251
- // Bounded FIFO eviction — never a blanket clear(), which would drop
1252
- // the id just added and let its ~500ms duplicate done re-fire.
1253
- while (this.completedTaskIds.size > 64) {
1254
- const oldest = this.completedTaskIds.values().next().value;
1255
- if (oldest === taskId)
1256
- break;
1257
- this.completedTaskIds.delete(oldest);
1258
- }
1259
- this.taskDone = true;
1260
1588
  const status = data['status'];
1261
1589
  const exitCode = data['exitCode'];
1262
- if (this.config.injectStatusLines !== false) {
1263
- this.emitLine(`[riff] 任务完成${status ? ` (${status}${exitCode != null ? `, exit=${exitCode}` : ''})` : ''}`, status === 'failed' ? 'warn' : 'ok');
1264
- }
1265
- // Fetch final output from task-detail API (SSE has no output events
1266
- // for runner tasks) BEFORE firing the turn boundary: the boundary
1267
- // flushes queued follow-ups → currentTaskId flips to the next task →
1268
- // the stale guard would (correctly) drop THIS task's only report.
1269
- if (status === 'completed' || status === 'failed') {
1270
- void this.fetchAndEmitOutput(taskId)
1271
- .catch(() => { })
1272
- .finally(() => { this.taskDoneCb?.(); });
1273
- }
1274
- else {
1275
- this.taskDoneCb?.();
1276
- }
1590
+ this.completeTask(taskId, status, exitCode);
1277
1591
  // NOTE: task done does NOT trigger onExit — session stays alive
1278
1592
  // for follow-up messages. Only /close or unrecoverable errors exit.
1279
1593
  break;