botmux 3.14.0 → 3.16.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 (812) hide show
  1. package/README.en.md +2 -2
  2. package/README.md +2 -2
  3. package/dist/.runtime-build-id +1 -1
  4. package/dist/adapters/backend/destroy-result.d.ts +159 -0
  5. package/dist/adapters/backend/destroy-result.d.ts.map +1 -0
  6. package/dist/adapters/backend/destroy-result.js +223 -0
  7. package/dist/adapters/backend/destroy-result.js.map +1 -0
  8. package/dist/adapters/backend/herdr-backend.d.ts +4 -0
  9. package/dist/adapters/backend/herdr-backend.d.ts.map +1 -1
  10. package/dist/adapters/backend/herdr-backend.js +23 -1
  11. package/dist/adapters/backend/herdr-backend.js.map +1 -1
  12. package/dist/adapters/backend/mojo-backend.d.ts +577 -0
  13. package/dist/adapters/backend/mojo-backend.d.ts.map +1 -0
  14. package/dist/adapters/backend/mojo-backend.js +2471 -0
  15. package/dist/adapters/backend/mojo-backend.js.map +1 -0
  16. package/dist/adapters/backend/mojo-budgets.d.ts +21 -0
  17. package/dist/adapters/backend/mojo-budgets.d.ts.map +1 -0
  18. package/dist/adapters/backend/mojo-budgets.js +22 -0
  19. package/dist/adapters/backend/mojo-budgets.js.map +1 -0
  20. package/dist/adapters/backend/mojo-isolated-workspace.d.ts +69 -0
  21. package/dist/adapters/backend/mojo-isolated-workspace.d.ts.map +1 -0
  22. package/dist/adapters/backend/mojo-isolated-workspace.js +213 -0
  23. package/dist/adapters/backend/mojo-isolated-workspace.js.map +1 -0
  24. package/dist/adapters/backend/mojo-process-tree.d.ts +292 -0
  25. package/dist/adapters/backend/mojo-process-tree.d.ts.map +1 -0
  26. package/dist/adapters/backend/mojo-process-tree.js +486 -0
  27. package/dist/adapters/backend/mojo-process-tree.js.map +1 -0
  28. package/dist/adapters/backend/mojo-types.d.ts +542 -0
  29. package/dist/adapters/backend/mojo-types.d.ts.map +1 -0
  30. package/dist/adapters/backend/mojo-types.js +682 -0
  31. package/dist/adapters/backend/mojo-types.js.map +1 -0
  32. package/dist/adapters/backend/reproduce-command.js +3 -3
  33. package/dist/adapters/backend/reproduce-command.js.map +1 -1
  34. package/dist/adapters/backend/riff-backend.js +1 -1
  35. package/dist/adapters/backend/riff-backend.js.map +1 -1
  36. package/dist/adapters/backend/sandbox.d.ts +44 -7
  37. package/dist/adapters/backend/sandbox.d.ts.map +1 -1
  38. package/dist/adapters/backend/sandbox.js +30 -7
  39. package/dist/adapters/backend/sandbox.js.map +1 -1
  40. package/dist/adapters/backend/session-backend-selector.d.ts +14 -1
  41. package/dist/adapters/backend/session-backend-selector.d.ts.map +1 -1
  42. package/dist/adapters/backend/session-backend-selector.js +82 -10
  43. package/dist/adapters/backend/session-backend-selector.js.map +1 -1
  44. package/dist/adapters/backend/tmux-pipe-backend.d.ts +30 -0
  45. package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -1
  46. package/dist/adapters/backend/tmux-pipe-backend.js +60 -0
  47. package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
  48. package/dist/adapters/backend/types.d.ts +108 -6
  49. package/dist/adapters/backend/types.d.ts.map +1 -1
  50. package/dist/adapters/backend/types.js.map +1 -1
  51. package/dist/adapters/backend/zmx-backend.d.ts +11 -0
  52. package/dist/adapters/backend/zmx-backend.d.ts.map +1 -1
  53. package/dist/adapters/backend/zmx-backend.js +16 -1
  54. package/dist/adapters/backend/zmx-backend.js.map +1 -1
  55. package/dist/adapters/cli/codex-app.d.ts.map +1 -1
  56. package/dist/adapters/cli/codex-app.js +2 -2
  57. package/dist/adapters/cli/codex-app.js.map +1 -1
  58. package/dist/adapters/cli/codex.d.ts.map +1 -1
  59. package/dist/adapters/cli/codex.js +7 -0
  60. package/dist/adapters/cli/codex.js.map +1 -1
  61. package/dist/adapters/cli/copilot.d.ts +3 -1
  62. package/dist/adapters/cli/copilot.d.ts.map +1 -1
  63. package/dist/adapters/cli/copilot.js +23 -5
  64. package/dist/adapters/cli/copilot.js.map +1 -1
  65. package/dist/adapters/cli/cursor.d.ts.map +1 -1
  66. package/dist/adapters/cli/cursor.js +26 -4
  67. package/dist/adapters/cli/cursor.js.map +1 -1
  68. package/dist/adapters/cli/dsh.d.ts.map +1 -1
  69. package/dist/adapters/cli/dsh.js +5 -1
  70. package/dist/adapters/cli/dsh.js.map +1 -1
  71. package/dist/adapters/cli/grok.d.ts.map +1 -1
  72. package/dist/adapters/cli/grok.js +24 -5
  73. package/dist/adapters/cli/grok.js.map +1 -1
  74. package/dist/adapters/cli/kimi.d.ts.map +1 -1
  75. package/dist/adapters/cli/kimi.js +19 -1
  76. package/dist/adapters/cli/kimi.js.map +1 -1
  77. package/dist/adapters/cli/mojo.d.ts +28 -0
  78. package/dist/adapters/cli/mojo.d.ts.map +1 -0
  79. package/dist/adapters/cli/mojo.js +58 -0
  80. package/dist/adapters/cli/mojo.js.map +1 -0
  81. package/dist/adapters/cli/pi.d.ts +20 -5
  82. package/dist/adapters/cli/pi.d.ts.map +1 -1
  83. package/dist/adapters/cli/pi.js +27 -5
  84. package/dist/adapters/cli/pi.js.map +1 -1
  85. package/dist/adapters/cli/read-isolation.d.ts +251 -1
  86. package/dist/adapters/cli/read-isolation.d.ts.map +1 -1
  87. package/dist/adapters/cli/read-isolation.js +254 -1
  88. package/dist/adapters/cli/read-isolation.js.map +1 -1
  89. package/dist/adapters/cli/registry.d.ts +13 -1
  90. package/dist/adapters/cli/registry.d.ts.map +1 -1
  91. package/dist/adapters/cli/registry.js +19 -1
  92. package/dist/adapters/cli/registry.js.map +1 -1
  93. package/dist/adapters/cli/runner-input.d.ts +3 -3
  94. package/dist/adapters/cli/runner-input.d.ts.map +1 -1
  95. package/dist/adapters/cli/runner-input.js +4 -3
  96. package/dist/adapters/cli/runner-input.js.map +1 -1
  97. package/dist/adapters/cli/shared-hints.d.ts +3 -1
  98. package/dist/adapters/cli/shared-hints.d.ts.map +1 -1
  99. package/dist/adapters/cli/shared-hints.js +51 -30
  100. package/dist/adapters/cli/shared-hints.js.map +1 -1
  101. package/dist/adapters/cli/types.d.ts +33 -4
  102. package/dist/adapters/cli/types.d.ts.map +1 -1
  103. package/dist/bot-registry.d.ts +42 -0
  104. package/dist/bot-registry.d.ts.map +1 -1
  105. package/dist/bot-registry.js +54 -4
  106. package/dist/bot-registry.js.map +1 -1
  107. package/dist/cli/arg-utils.d.ts +5 -0
  108. package/dist/cli/arg-utils.d.ts.map +1 -1
  109. package/dist/cli/arg-utils.js +7 -0
  110. package/dist/cli/arg-utils.js.map +1 -1
  111. package/dist/cli/bots-list-output.d.ts +130 -2
  112. package/dist/cli/bots-list-output.d.ts.map +1 -1
  113. package/dist/cli/bots-list-output.js +173 -27
  114. package/dist/cli/bots-list-output.js.map +1 -1
  115. package/dist/cli/daemon-lifecycle-env.d.ts +10 -3
  116. package/dist/cli/daemon-lifecycle-env.d.ts.map +1 -1
  117. package/dist/cli/daemon-lifecycle-env.js +38 -11
  118. package/dist/cli/daemon-lifecycle-env.js.map +1 -1
  119. package/dist/cli/dashboard-command.d.ts +15 -0
  120. package/dist/cli/dashboard-command.d.ts.map +1 -1
  121. package/dist/cli/dashboard-command.js +22 -0
  122. package/dist/cli/dashboard-command.js.map +1 -1
  123. package/dist/cli/log-tail.d.ts +54 -0
  124. package/dist/cli/log-tail.d.ts.map +1 -0
  125. package/dist/cli/log-tail.js +167 -0
  126. package/dist/cli/log-tail.js.map +1 -0
  127. package/dist/cli/pm2-env.d.ts +16 -0
  128. package/dist/cli/pm2-env.d.ts.map +1 -0
  129. package/dist/cli/pm2-env.js +63 -0
  130. package/dist/cli/pm2-env.js.map +1 -0
  131. package/dist/cli/pm2-fleet-lock.d.ts +26 -0
  132. package/dist/cli/pm2-fleet-lock.d.ts.map +1 -0
  133. package/dist/cli/pm2-fleet-lock.js +50 -0
  134. package/dist/cli/pm2-fleet-lock.js.map +1 -0
  135. package/dist/cli/pm2-god-retirement.d.ts +59 -0
  136. package/dist/cli/pm2-god-retirement.d.ts.map +1 -0
  137. package/dist/cli/pm2-god-retirement.js +95 -0
  138. package/dist/cli/pm2-god-retirement.js.map +1 -0
  139. package/dist/cli/session-list-wake.d.ts +41 -0
  140. package/dist/cli/session-list-wake.d.ts.map +1 -0
  141. package/dist/cli/session-list-wake.js +102 -0
  142. package/dist/cli/session-list-wake.js.map +1 -0
  143. package/dist/cli/workflow.d.ts.map +1 -1
  144. package/dist/cli/workflow.js +26 -0
  145. package/dist/cli/workflow.js.map +1 -1
  146. package/dist/cli.d.ts.map +1 -1
  147. package/dist/cli.js +941 -118
  148. package/dist/cli.js.map +1 -1
  149. package/dist/codex-app-runner.js +261 -49
  150. package/dist/codex-app-runner.js.map +1 -1
  151. package/dist/core/cli-runtime-update.d.ts +8 -0
  152. package/dist/core/cli-runtime-update.d.ts.map +1 -1
  153. package/dist/core/cli-runtime-update.js +127 -8
  154. package/dist/core/cli-runtime-update.js.map +1 -1
  155. package/dist/core/close-residual.d.ts +64 -0
  156. package/dist/core/close-residual.d.ts.map +1 -0
  157. package/dist/core/close-residual.js +93 -0
  158. package/dist/core/close-residual.js.map +1 -0
  159. package/dist/core/closed-session-card.d.ts.map +1 -1
  160. package/dist/core/closed-session-card.js +11 -3
  161. package/dist/core/closed-session-card.js.map +1 -1
  162. package/dist/core/command-discovery.d.ts.map +1 -1
  163. package/dist/core/command-discovery.js +4 -1
  164. package/dist/core/command-discovery.js.map +1 -1
  165. package/dist/core/command-handler.d.ts.map +1 -1
  166. package/dist/core/command-handler.js +129 -27
  167. package/dist/core/command-handler.js.map +1 -1
  168. package/dist/core/cost-calculator.d.ts +3 -1
  169. package/dist/core/cost-calculator.d.ts.map +1 -1
  170. package/dist/core/cost-calculator.js +230 -1
  171. package/dist/core/cost-calculator.js.map +1 -1
  172. package/dist/core/daemon-background-close.d.ts +19 -0
  173. package/dist/core/daemon-background-close.d.ts.map +1 -0
  174. package/dist/core/daemon-background-close.js +39 -0
  175. package/dist/core/daemon-background-close.js.map +1 -0
  176. package/dist/core/dashboard-command/overview.d.ts +4 -0
  177. package/dist/core/dashboard-command/overview.d.ts.map +1 -1
  178. package/dist/core/dashboard-command/overview.js +8 -1
  179. package/dist/core/dashboard-command/overview.js.map +1 -1
  180. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  181. package/dist/core/dashboard-ipc-server.js +415 -42
  182. package/dist/core/dashboard-ipc-server.js.map +1 -1
  183. package/dist/core/dashboard-rows.d.ts +4 -0
  184. package/dist/core/dashboard-rows.d.ts.map +1 -1
  185. package/dist/core/dashboard-rows.js +3 -0
  186. package/dist/core/dashboard-rows.js.map +1 -1
  187. package/dist/core/dashboard-url.d.ts +37 -0
  188. package/dist/core/dashboard-url.d.ts.map +1 -1
  189. package/dist/core/dashboard-url.js +71 -0
  190. package/dist/core/dashboard-url.js.map +1 -1
  191. package/dist/core/deferred-schedule-settlement.d.ts +15 -1
  192. package/dist/core/deferred-schedule-settlement.d.ts.map +1 -1
  193. package/dist/core/deferred-schedule-settlement.js +4 -1
  194. package/dist/core/deferred-schedule-settlement.js.map +1 -1
  195. package/dist/core/device-isolation-daemon.d.ts +91 -1
  196. package/dist/core/device-isolation-daemon.d.ts.map +1 -1
  197. package/dist/core/device-isolation-daemon.js +346 -2
  198. package/dist/core/device-isolation-daemon.js.map +1 -1
  199. package/dist/core/local-terminal-opener.d.ts.map +1 -1
  200. package/dist/core/local-terminal-opener.js +5 -1
  201. package/dist/core/local-terminal-opener.js.map +1 -1
  202. package/dist/core/loopback-target.d.ts +14 -0
  203. package/dist/core/loopback-target.d.ts.map +1 -0
  204. package/dist/core/loopback-target.js +20 -0
  205. package/dist/core/loopback-target.js.map +1 -0
  206. package/dist/core/maintenance.d.ts.map +1 -1
  207. package/dist/core/maintenance.js +16 -2
  208. package/dist/core/maintenance.js.map +1 -1
  209. package/dist/core/mojo-containment-command.d.ts +20 -0
  210. package/dist/core/mojo-containment-command.d.ts.map +1 -0
  211. package/dist/core/mojo-containment-command.js +266 -0
  212. package/dist/core/mojo-containment-command.js.map +1 -0
  213. package/dist/core/mojo-containment.d.ts +575 -0
  214. package/dist/core/mojo-containment.d.ts.map +1 -0
  215. package/dist/core/mojo-containment.js +1158 -0
  216. package/dist/core/mojo-containment.js.map +1 -0
  217. package/dist/core/mojo-launcher-env-quarantine.d.ts +30 -0
  218. package/dist/core/mojo-launcher-env-quarantine.d.ts.map +1 -0
  219. package/dist/core/mojo-launcher-env-quarantine.js +175 -0
  220. package/dist/core/mojo-launcher-env-quarantine.js.map +1 -0
  221. package/dist/core/mojo-session-identity.d.ts +3 -0
  222. package/dist/core/mojo-session-identity.d.ts.map +1 -0
  223. package/dist/core/mojo-session-identity.js +126 -0
  224. package/dist/core/mojo-session-identity.js.map +1 -0
  225. package/dist/core/persistent-backend.d.ts +32 -8
  226. package/dist/core/persistent-backend.d.ts.map +1 -1
  227. package/dist/core/persistent-backend.js +55 -17
  228. package/dist/core/persistent-backend.js.map +1 -1
  229. package/dist/core/plugins/mcp/gateway.d.ts +14 -1
  230. package/dist/core/plugins/mcp/gateway.d.ts.map +1 -1
  231. package/dist/core/plugins/mcp/gateway.js +70 -3
  232. package/dist/core/plugins/mcp/gateway.js.map +1 -1
  233. package/dist/core/plugins/mcp/host.d.ts +2 -0
  234. package/dist/core/plugins/mcp/host.d.ts.map +1 -1
  235. package/dist/core/plugins/mcp/host.js +2 -0
  236. package/dist/core/plugins/mcp/host.js.map +1 -1
  237. package/dist/core/plugins/pm2.d.ts.map +1 -1
  238. package/dist/core/plugins/pm2.js +16 -3
  239. package/dist/core/plugins/pm2.js.map +1 -1
  240. package/dist/core/plugins/service-manager.d.ts +9 -0
  241. package/dist/core/plugins/service-manager.d.ts.map +1 -1
  242. package/dist/core/plugins/service-manager.js +33 -16
  243. package/dist/core/plugins/service-manager.js.map +1 -1
  244. package/dist/core/preview-port-owner.d.ts +109 -0
  245. package/dist/core/preview-port-owner.d.ts.map +1 -0
  246. package/dist/core/preview-port-owner.js +320 -0
  247. package/dist/core/preview-port-owner.js.map +1 -0
  248. package/dist/core/remote-cli-ids.d.ts +5 -0
  249. package/dist/core/remote-cli-ids.d.ts.map +1 -0
  250. package/dist/core/remote-cli-ids.js +24 -0
  251. package/dist/core/remote-cli-ids.js.map +1 -0
  252. package/dist/core/{riff-shutdown-detach.d.ts → remote-shutdown-detach.d.ts} +40 -39
  253. package/dist/core/remote-shutdown-detach.d.ts.map +1 -0
  254. package/dist/core/{riff-shutdown-detach.js → remote-shutdown-detach.js} +85 -83
  255. package/dist/core/remote-shutdown-detach.js.map +1 -0
  256. package/dist/core/{riff-worker-shutdown-readiness.d.ts → remote-worker-shutdown-readiness.d.ts} +5 -4
  257. package/dist/core/remote-worker-shutdown-readiness.d.ts.map +1 -0
  258. package/dist/core/{riff-worker-shutdown-readiness.js → remote-worker-shutdown-readiness.js} +4 -3
  259. package/dist/core/remote-worker-shutdown-readiness.js.map +1 -0
  260. package/dist/core/reply-target.d.ts +16 -1
  261. package/dist/core/reply-target.d.ts.map +1 -1
  262. package/dist/core/reply-target.js +61 -0
  263. package/dist/core/reply-target.js.map +1 -1
  264. package/dist/core/session-group-birth.d.ts +26 -0
  265. package/dist/core/session-group-birth.d.ts.map +1 -1
  266. package/dist/core/session-group-birth.js +81 -4
  267. package/dist/core/session-group-birth.js.map +1 -1
  268. package/dist/core/session-manager.d.ts +15 -2
  269. package/dist/core/session-manager.d.ts.map +1 -1
  270. package/dist/core/session-manager.js +259 -5
  271. package/dist/core/session-manager.js.map +1 -1
  272. package/dist/core/session-model.d.ts +46 -0
  273. package/dist/core/session-model.d.ts.map +1 -0
  274. package/dist/core/session-model.js +42 -0
  275. package/dist/core/session-model.js.map +1 -0
  276. package/dist/core/session-owner-reminder.d.ts +63 -0
  277. package/dist/core/session-owner-reminder.d.ts.map +1 -0
  278. package/dist/core/session-owner-reminder.js +196 -0
  279. package/dist/core/session-owner-reminder.js.map +1 -0
  280. package/dist/core/session-preview-registry.d.ts +53 -0
  281. package/dist/core/session-preview-registry.d.ts.map +1 -0
  282. package/dist/core/session-preview-registry.js +82 -0
  283. package/dist/core/session-preview-registry.js.map +1 -0
  284. package/dist/core/session-preview.d.ts +114 -0
  285. package/dist/core/session-preview.d.ts.map +1 -0
  286. package/dist/core/session-preview.js +184 -0
  287. package/dist/core/session-preview.js.map +1 -0
  288. package/dist/core/session-rename.d.ts.map +1 -1
  289. package/dist/core/session-rename.js +5 -3
  290. package/dist/core/session-rename.js.map +1 -1
  291. package/dist/core/session-wake-deadline.d.ts +5 -0
  292. package/dist/core/session-wake-deadline.d.ts.map +1 -0
  293. package/dist/core/session-wake-deadline.js +13 -0
  294. package/dist/core/session-wake-deadline.js.map +1 -0
  295. package/dist/core/shutdown-budgets.d.ts +7 -6
  296. package/dist/core/shutdown-budgets.d.ts.map +1 -1
  297. package/dist/core/shutdown-budgets.js +11 -10
  298. package/dist/core/shutdown-budgets.js.map +1 -1
  299. package/dist/core/skills/frontmatter.d.ts.map +1 -1
  300. package/dist/core/skills/frontmatter.js +6 -1
  301. package/dist/core/skills/frontmatter.js.map +1 -1
  302. package/dist/core/terminal-control-grant.d.ts +74 -0
  303. package/dist/core/terminal-control-grant.d.ts.map +1 -0
  304. package/dist/core/terminal-control-grant.js +183 -0
  305. package/dist/core/terminal-control-grant.js.map +1 -0
  306. package/dist/core/terminal-proxy.d.ts.map +1 -1
  307. package/dist/core/terminal-proxy.js +4 -5
  308. package/dist/core/terminal-proxy.js.map +1 -1
  309. package/dist/core/terminal-write-auth.d.ts +4 -11
  310. package/dist/core/terminal-write-auth.d.ts.map +1 -1
  311. package/dist/core/terminal-write-auth.js +19 -16
  312. package/dist/core/terminal-write-auth.js.map +1 -1
  313. package/dist/core/trigger-session.d.ts.map +1 -1
  314. package/dist/core/trigger-session.js +36 -29
  315. package/dist/core/trigger-session.js.map +1 -1
  316. package/dist/core/types.d.ts +88 -10
  317. package/dist/core/types.d.ts.map +1 -1
  318. package/dist/core/types.js +9 -7
  319. package/dist/core/types.js.map +1 -1
  320. package/dist/core/workbench-link.d.ts +81 -0
  321. package/dist/core/workbench-link.d.ts.map +1 -0
  322. package/dist/core/workbench-link.js +109 -0
  323. package/dist/core/workbench-link.js.map +1 -0
  324. package/dist/core/worker-pool.d.ts +312 -11
  325. package/dist/core/worker-pool.d.ts.map +1 -1
  326. package/dist/core/worker-pool.js +2515 -217
  327. package/dist/core/worker-pool.js.map +1 -1
  328. package/dist/daemon.d.ts +6 -0
  329. package/dist/daemon.d.ts.map +1 -1
  330. package/dist/daemon.js +447 -116
  331. package/dist/daemon.js.map +1 -1
  332. package/dist/dashboard/aggregator.d.ts +33 -3
  333. package/dist/dashboard/aggregator.d.ts.map +1 -1
  334. package/dist/dashboard/aggregator.js +93 -17
  335. package/dist/dashboard/aggregator.js.map +1 -1
  336. package/dist/dashboard/auth-session-connections.d.ts +59 -0
  337. package/dist/dashboard/auth-session-connections.d.ts.map +1 -0
  338. package/dist/dashboard/auth-session-connections.js +109 -0
  339. package/dist/dashboard/auth-session-connections.js.map +1 -0
  340. package/dist/dashboard/auth.d.ts +99 -1
  341. package/dist/dashboard/auth.d.ts.map +1 -1
  342. package/dist/dashboard/auth.js +176 -64
  343. package/dist/dashboard/auth.js.map +1 -1
  344. package/dist/dashboard/bot-payload.d.ts +5 -0
  345. package/dist/dashboard/bot-payload.d.ts.map +1 -1
  346. package/dist/dashboard/bot-payload.js +4 -0
  347. package/dist/dashboard/bot-payload.js.map +1 -1
  348. package/dist/dashboard/control-audit.d.ts +112 -0
  349. package/dist/dashboard/control-audit.d.ts.map +1 -0
  350. package/dist/dashboard/control-audit.js +195 -0
  351. package/dist/dashboard/control-audit.js.map +1 -0
  352. package/dist/dashboard/control-csrf.d.ts +123 -0
  353. package/dist/dashboard/control-csrf.d.ts.map +1 -0
  354. package/dist/dashboard/control-csrf.js +392 -0
  355. package/dist/dashboard/control-csrf.js.map +1 -0
  356. package/dist/dashboard/daemon-reconcile.d.ts +26 -0
  357. package/dist/dashboard/daemon-reconcile.d.ts.map +1 -0
  358. package/dist/dashboard/daemon-reconcile.js +46 -0
  359. package/dist/dashboard/daemon-reconcile.js.map +1 -0
  360. package/dist/dashboard/debug-terminal.d.ts.map +1 -1
  361. package/dist/dashboard/debug-terminal.js +18 -4
  362. package/dist/dashboard/debug-terminal.js.map +1 -1
  363. package/dist/dashboard/events-sse.d.ts +60 -0
  364. package/dist/dashboard/events-sse.d.ts.map +1 -0
  365. package/dist/dashboard/events-sse.js +63 -0
  366. package/dist/dashboard/events-sse.js.map +1 -0
  367. package/dist/dashboard/h5-auth.d.ts +256 -0
  368. package/dist/dashboard/h5-auth.d.ts.map +1 -0
  369. package/dist/dashboard/h5-auth.js +825 -0
  370. package/dist/dashboard/h5-auth.js.map +1 -0
  371. package/dist/dashboard/managed-spawn.d.ts +36 -0
  372. package/dist/dashboard/managed-spawn.d.ts.map +1 -0
  373. package/dist/dashboard/managed-spawn.js +169 -0
  374. package/dist/dashboard/managed-spawn.js.map +1 -0
  375. package/dist/dashboard/preview-content-capability.d.ts +41 -0
  376. package/dist/dashboard/preview-content-capability.d.ts.map +1 -0
  377. package/dist/dashboard/preview-content-capability.js +127 -0
  378. package/dist/dashboard/preview-content-capability.js.map +1 -0
  379. package/dist/dashboard/preview-contract.d.ts +85 -0
  380. package/dist/dashboard/preview-contract.d.ts.map +1 -0
  381. package/dist/dashboard/preview-contract.js +198 -0
  382. package/dist/dashboard/preview-contract.js.map +1 -0
  383. package/dist/dashboard/preview-guard-page.d.ts +52 -0
  384. package/dist/dashboard/preview-guard-page.d.ts.map +1 -0
  385. package/dist/dashboard/preview-guard-page.js +162 -0
  386. package/dist/dashboard/preview-guard-page.js.map +1 -0
  387. package/dist/dashboard/preview-interaction.d.ts +57 -0
  388. package/dist/dashboard/preview-interaction.d.ts.map +1 -0
  389. package/dist/dashboard/preview-interaction.js +198 -0
  390. package/dist/dashboard/preview-interaction.js.map +1 -0
  391. package/dist/dashboard/preview-proxy.d.ts +113 -0
  392. package/dist/dashboard/preview-proxy.d.ts.map +1 -0
  393. package/dist/dashboard/preview-proxy.js +543 -0
  394. package/dist/dashboard/preview-proxy.js.map +1 -0
  395. package/dist/dashboard/public-redact.d.ts +23 -0
  396. package/dist/dashboard/public-redact.d.ts.map +1 -1
  397. package/dist/dashboard/public-redact.js +90 -21
  398. package/dist/dashboard/public-redact.js.map +1 -1
  399. package/dist/dashboard/request-identity.d.ts +77 -0
  400. package/dist/dashboard/request-identity.d.ts.map +1 -0
  401. package/dist/dashboard/request-identity.js +72 -0
  402. package/dist/dashboard/request-identity.js.map +1 -0
  403. package/dist/dashboard/session-cleanup.d.ts +5 -0
  404. package/dist/dashboard/session-cleanup.d.ts.map +1 -1
  405. package/dist/dashboard/session-cleanup.js +4 -0
  406. package/dist/dashboard/session-cleanup.js.map +1 -1
  407. package/dist/dashboard/settings-write-applier.d.ts +5 -1
  408. package/dist/dashboard/settings-write-applier.d.ts.map +1 -1
  409. package/dist/dashboard/settings-write-applier.js +14 -0
  410. package/dist/dashboard/settings-write-applier.js.map +1 -1
  411. package/dist/dashboard/standing-link.d.ts +76 -0
  412. package/dist/dashboard/standing-link.d.ts.map +1 -0
  413. package/dist/dashboard/standing-link.js +111 -0
  414. package/dist/dashboard/standing-link.js.map +1 -0
  415. package/dist/dashboard/terminal-control.d.ts +105 -0
  416. package/dist/dashboard/terminal-control.d.ts.map +1 -0
  417. package/dist/dashboard/terminal-control.js +296 -0
  418. package/dist/dashboard/terminal-control.js.map +1 -0
  419. package/dist/dashboard/terminal-front-proxy.d.ts +55 -0
  420. package/dist/dashboard/terminal-front-proxy.d.ts.map +1 -0
  421. package/dist/dashboard/terminal-front-proxy.js +397 -0
  422. package/dist/dashboard/terminal-front-proxy.js.map +1 -0
  423. package/dist/dashboard/terminal-view-capability.d.ts +97 -0
  424. package/dist/dashboard/terminal-view-capability.d.ts.map +1 -0
  425. package/dist/dashboard/terminal-view-capability.js +145 -0
  426. package/dist/dashboard/terminal-view-capability.js.map +1 -0
  427. package/dist/dashboard/web/agent-workbench-api.d.ts +59 -0
  428. package/dist/dashboard/web/agent-workbench-api.d.ts.map +1 -0
  429. package/dist/dashboard/web/agent-workbench-api.js +227 -0
  430. package/dist/dashboard/web/agent-workbench-api.js.map +1 -0
  431. package/dist/dashboard/web/agent-workbench-appearance-menu.d.ts +59 -0
  432. package/dist/dashboard/web/agent-workbench-appearance-menu.d.ts.map +1 -0
  433. package/dist/dashboard/web/agent-workbench-appearance-menu.js +195 -0
  434. package/dist/dashboard/web/agent-workbench-appearance-menu.js.map +1 -0
  435. package/dist/dashboard/web/agent-workbench-appearance.d.ts +222 -0
  436. package/dist/dashboard/web/agent-workbench-appearance.d.ts.map +1 -0
  437. package/dist/dashboard/web/agent-workbench-appearance.js +499 -0
  438. package/dist/dashboard/web/agent-workbench-appearance.js.map +1 -0
  439. package/dist/dashboard/web/agent-workbench-capabilities.d.ts +13 -0
  440. package/dist/dashboard/web/agent-workbench-capabilities.d.ts.map +1 -0
  441. package/dist/dashboard/web/agent-workbench-capabilities.js +24 -0
  442. package/dist/dashboard/web/agent-workbench-capabilities.js.map +1 -0
  443. package/dist/dashboard/web/agent-workbench-chat.d.ts +90 -0
  444. package/dist/dashboard/web/agent-workbench-chat.d.ts.map +1 -0
  445. package/dist/dashboard/web/agent-workbench-chat.js +168 -0
  446. package/dist/dashboard/web/agent-workbench-chat.js.map +1 -0
  447. package/dist/dashboard/web/agent-workbench-dock-page.d.ts +3 -0
  448. package/dist/dashboard/web/agent-workbench-dock-page.d.ts.map +1 -0
  449. package/dist/dashboard/web/agent-workbench-dock-page.js +23 -0
  450. package/dist/dashboard/web/agent-workbench-dock-page.js.map +1 -0
  451. package/dist/dashboard/web/agent-workbench-dock-view.d.ts +22 -0
  452. package/dist/dashboard/web/agent-workbench-dock-view.d.ts.map +1 -0
  453. package/dist/dashboard/web/agent-workbench-dock-view.js +76 -0
  454. package/dist/dashboard/web/agent-workbench-dock-view.js.map +1 -0
  455. package/dist/dashboard/web/agent-workbench-model.d.ts +196 -0
  456. package/dist/dashboard/web/agent-workbench-model.d.ts.map +1 -0
  457. package/dist/dashboard/web/agent-workbench-model.js +529 -0
  458. package/dist/dashboard/web/agent-workbench-model.js.map +1 -0
  459. package/dist/dashboard/web/agent-workbench-page.d.ts +3 -0
  460. package/dist/dashboard/web/agent-workbench-page.d.ts.map +1 -0
  461. package/dist/dashboard/web/agent-workbench-page.js +27 -0
  462. package/dist/dashboard/web/agent-workbench-page.js.map +1 -0
  463. package/dist/dashboard/web/agent-workbench-panes.d.ts +49 -0
  464. package/dist/dashboard/web/agent-workbench-panes.d.ts.map +1 -0
  465. package/dist/dashboard/web/agent-workbench-panes.js +468 -0
  466. package/dist/dashboard/web/agent-workbench-panes.js.map +1 -0
  467. package/dist/dashboard/web/agent-workbench-session-list.d.ts +42 -0
  468. package/dist/dashboard/web/agent-workbench-session-list.d.ts.map +1 -0
  469. package/dist/dashboard/web/agent-workbench-session-list.js +353 -0
  470. package/dist/dashboard/web/agent-workbench-session-list.js.map +1 -0
  471. package/dist/dashboard/web/agent-workbench-storage.d.ts +49 -0
  472. package/dist/dashboard/web/agent-workbench-storage.d.ts.map +1 -0
  473. package/dist/dashboard/web/agent-workbench-storage.js +197 -0
  474. package/dist/dashboard/web/agent-workbench-storage.js.map +1 -0
  475. package/dist/dashboard/web/agent-workbench-touch.d.ts +46 -0
  476. package/dist/dashboard/web/agent-workbench-touch.d.ts.map +1 -0
  477. package/dist/dashboard/web/agent-workbench-touch.js +96 -0
  478. package/dist/dashboard/web/agent-workbench-touch.js.map +1 -0
  479. package/dist/dashboard/web/agent-workbench-view.d.ts +34 -0
  480. package/dist/dashboard/web/agent-workbench-view.d.ts.map +1 -0
  481. package/dist/dashboard/web/agent-workbench-view.js +439 -0
  482. package/dist/dashboard/web/agent-workbench-view.js.map +1 -0
  483. package/dist/dashboard/web/app.d.ts.map +1 -1
  484. package/dist/dashboard/web/app.js +63 -2
  485. package/dist/dashboard/web/app.js.map +1 -1
  486. package/dist/dashboard/web/bot-defaults-page.d.ts +18 -0
  487. package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -1
  488. package/dist/dashboard/web/bot-defaults-page.js +362 -36
  489. package/dist/dashboard/web/bot-defaults-page.js.map +1 -1
  490. package/dist/dashboard/web/bot-defaults.d.ts +8 -0
  491. package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
  492. package/dist/dashboard/web/bot-defaults.js.map +1 -1
  493. package/dist/dashboard/web/control-csrf.d.ts +4 -0
  494. package/dist/dashboard/web/control-csrf.d.ts.map +1 -0
  495. package/dist/dashboard/web/control-csrf.js +26 -0
  496. package/dist/dashboard/web/control-csrf.js.map +1 -0
  497. package/dist/dashboard/web/dashboard-routes.d.ts.map +1 -1
  498. package/dist/dashboard/web/dashboard-routes.js +2 -0
  499. package/dist/dashboard/web/dashboard-routes.js.map +1 -1
  500. package/dist/dashboard/web/groups-page.d.ts.map +1 -1
  501. package/dist/dashboard/web/groups-page.js +11 -2
  502. package/dist/dashboard/web/groups-page.js.map +1 -1
  503. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  504. package/dist/dashboard/web/i18n.js +140 -6
  505. package/dist/dashboard/web/i18n.js.map +1 -1
  506. package/dist/dashboard/web/overview-page.d.ts.map +1 -1
  507. package/dist/dashboard/web/overview-page.js +3 -2
  508. package/dist/dashboard/web/overview-page.js.map +1 -1
  509. package/dist/dashboard/web/sessions-page.d.ts.map +1 -1
  510. package/dist/dashboard/web/sessions-page.js +41 -1
  511. package/dist/dashboard/web/sessions-page.js.map +1 -1
  512. package/dist/dashboard/web/sessions.d.ts.map +1 -1
  513. package/dist/dashboard/web/sessions.js +5 -1
  514. package/dist/dashboard/web/sessions.js.map +1 -1
  515. package/dist/dashboard/web/settings-page.d.ts +3 -0
  516. package/dist/dashboard/web/settings-page.d.ts.map +1 -1
  517. package/dist/dashboard/web/settings-page.js +56 -10
  518. package/dist/dashboard/web/settings-page.js.map +1 -1
  519. package/dist/dashboard/web/skills-page.d.ts.map +1 -1
  520. package/dist/dashboard/web/skills-page.js +4 -2
  521. package/dist/dashboard/web/skills-page.js.map +1 -1
  522. package/dist/dashboard/web/store.d.ts +27 -2
  523. package/dist/dashboard/web/store.d.ts.map +1 -1
  524. package/dist/dashboard/web/store.js +43 -4
  525. package/dist/dashboard/web/store.js.map +1 -1
  526. package/dist/dashboard/web/ui.d.ts +3 -0
  527. package/dist/dashboard/web/ui.d.ts.map +1 -1
  528. package/dist/dashboard/web/ui.js +11 -0
  529. package/dist/dashboard/web/ui.js.map +1 -1
  530. package/dist/dashboard/web/update-action.d.ts +12 -0
  531. package/dist/dashboard/web/update-action.d.ts.map +1 -1
  532. package/dist/dashboard/web/update-action.js +11 -0
  533. package/dist/dashboard/web/update-action.js.map +1 -1
  534. package/dist/dashboard/workbench-doctor.d.ts +114 -0
  535. package/dist/dashboard/workbench-doctor.d.ts.map +1 -0
  536. package/dist/dashboard/workbench-doctor.js +620 -0
  537. package/dist/dashboard/workbench-doctor.js.map +1 -0
  538. package/dist/dashboard/workbench-ticket.d.ts +165 -0
  539. package/dist/dashboard/workbench-ticket.d.ts.map +1 -0
  540. package/dist/dashboard/workbench-ticket.js +495 -0
  541. package/dist/dashboard/workbench-ticket.js.map +1 -0
  542. package/dist/dashboard-web/app.js +1 -1
  543. package/dist/dashboard-web/chunks/agent-workbench-dock-page-IKAC5CFL.js +1 -0
  544. package/dist/dashboard-web/chunks/agent-workbench-page-M53UN5EB.js +1 -0
  545. package/dist/dashboard-web/chunks/bot-defaults-page-YKE5SDZF.js +10 -0
  546. package/dist/dashboard-web/chunks/chunk-22WIADUT.js +2 -0
  547. package/dist/dashboard-web/chunks/chunk-24ONYQMR.js +1 -0
  548. package/dist/dashboard-web/chunks/{chunk-6O7KXFD2.js → chunk-2W6SCN3M.js} +1 -1
  549. package/dist/dashboard-web/chunks/chunk-4XNJX6B3.js +1 -0
  550. package/dist/dashboard-web/chunks/{chunk-Q2MT4VWH.js → chunk-F6KNPF4R.js} +11 -9
  551. package/dist/dashboard-web/chunks/chunk-FXW3V4JR.js +1 -0
  552. package/dist/dashboard-web/chunks/chunk-G5ZS2YUS.js +1 -0
  553. package/dist/dashboard-web/chunks/chunk-KTQ7B6ZR.js +1 -0
  554. package/dist/dashboard-web/chunks/{chunk-U3ZFSO7E.js → chunk-NACP7BMR.js} +1 -1
  555. package/dist/dashboard-web/chunks/{connectors-page-2KZO5KSJ.js → connectors-page-VOISXXII.js} +1 -1
  556. package/dist/dashboard-web/chunks/{feedback-page-CLVTOLLO.js → feedback-page-RN6HRZMS.js} +1 -1
  557. package/dist/dashboard-web/chunks/groups-page-UQT6DCB7.js +8 -0
  558. package/dist/dashboard-web/chunks/{insights-page-4RPU2CRU.js → insights-page-HJFDYR5N.js} +3 -3
  559. package/dist/dashboard-web/chunks/{monitor-room-OTKQ2CXK.js → monitor-room-V7PCP47V.js} +1 -1
  560. package/dist/dashboard-web/chunks/{monitoring-page-IJPUB6JT.js → monitoring-page-NKEKVQAP.js} +1 -1
  561. package/dist/dashboard-web/chunks/{office-page-QSR3J44Y.js → office-page-GVJF73XW.js} +1 -1
  562. package/dist/dashboard-web/chunks/overview-page-KHLV6VTI.js +1 -0
  563. package/dist/dashboard-web/chunks/{plugin-page-VEGTCEAS.js → plugin-page-J3OF5IY4.js} +1 -1
  564. package/dist/dashboard-web/chunks/{roles-page-6NWJMOB5.js → roles-page-WQIRSOLZ.js} +1 -1
  565. package/dist/dashboard-web/chunks/{schedules-page-6I3LNXD4.js → schedules-page-J2DEY2X6.js} +1 -1
  566. package/dist/dashboard-web/chunks/sessions-page-7BQFEA5P.js +8 -0
  567. package/dist/dashboard-web/chunks/settings-page-BJEIOFJH.js +2 -0
  568. package/dist/dashboard-web/chunks/{skills-page-M2Q6L5CB.js → skills-page-HGIFR3IU.js} +2 -2
  569. package/dist/dashboard-web/chunks/{team-federation-page-7I7FDE3B.js → team-federation-page-MR5ZPL4B.js} +1 -1
  570. package/dist/dashboard-web/chunks/{v3-page-HQXRL65L.js → v3-page-GBR5JWPE.js} +2 -2
  571. package/dist/dashboard-web/chunks/{whiteboards-page-LQKC4TAZ.js → whiteboards-page-QAZM4BN3.js} +1 -1
  572. package/dist/dashboard-web/index.html +8 -1
  573. package/dist/dashboard-web/style.css +2006 -10
  574. package/dist/dashboard.js +1017 -264
  575. package/dist/dashboard.js.map +1 -1
  576. package/dist/dsh-runner.js +8 -1
  577. package/dist/dsh-runner.js.map +1 -1
  578. package/dist/global-config.d.ts +30 -0
  579. package/dist/global-config.d.ts.map +1 -1
  580. package/dist/global-config.js +33 -0
  581. package/dist/global-config.js.map +1 -1
  582. package/dist/i18n/en.d.ts.map +1 -1
  583. package/dist/i18n/en.js +46 -27
  584. package/dist/i18n/en.js.map +1 -1
  585. package/dist/i18n/zh.d.ts.map +1 -1
  586. package/dist/i18n/zh.js +43 -27
  587. package/dist/i18n/zh.js.map +1 -1
  588. package/dist/im/lark/card-builder.d.ts +1 -1
  589. package/dist/im/lark/card-builder.d.ts.map +1 -1
  590. package/dist/im/lark/card-builder.js +22 -6
  591. package/dist/im/lark/card-builder.js.map +1 -1
  592. package/dist/im/lark/card-handler.d.ts.map +1 -1
  593. package/dist/im/lark/card-handler.js +118 -24
  594. package/dist/im/lark/card-handler.js.map +1 -1
  595. package/dist/im/lark/client.d.ts +15 -0
  596. package/dist/im/lark/client.d.ts.map +1 -1
  597. package/dist/im/lark/client.js +30 -0
  598. package/dist/im/lark/client.js.map +1 -1
  599. package/dist/im/lark/event-dispatcher.d.ts +48 -0
  600. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  601. package/dist/im/lark/event-dispatcher.js +138 -41
  602. package/dist/im/lark/event-dispatcher.js.map +1 -1
  603. package/dist/im/lark/identity-cache.d.ts +14 -6
  604. package/dist/im/lark/identity-cache.d.ts.map +1 -1
  605. package/dist/im/lark/identity-cache.js +69 -26
  606. package/dist/im/lark/identity-cache.js.map +1 -1
  607. package/dist/im/lark/lark-hosts.d.ts +13 -0
  608. package/dist/im/lark/lark-hosts.d.ts.map +1 -1
  609. package/dist/im/lark/lark-hosts.js +16 -0
  610. package/dist/im/lark/lark-hosts.js.map +1 -1
  611. package/dist/im/lark/overview-card.d.ts +22 -0
  612. package/dist/im/lark/overview-card.d.ts.map +1 -1
  613. package/dist/im/lark/overview-card.js +53 -3
  614. package/dist/im/lark/overview-card.js.map +1 -1
  615. package/dist/im/lark/sessions-card.d.ts.map +1 -1
  616. package/dist/im/lark/sessions-card.js +26 -3
  617. package/dist/im/lark/sessions-card.js.map +1 -1
  618. package/dist/im/lark/workflow-slash-command.d.ts +6 -0
  619. package/dist/im/lark/workflow-slash-command.d.ts.map +1 -1
  620. package/dist/im/lark/workflow-slash-command.js +7 -0
  621. package/dist/im/lark/workflow-slash-command.js.map +1 -1
  622. package/dist/index-daemon.js +27 -4
  623. package/dist/index-daemon.js.map +1 -1
  624. package/dist/index-dashboard.d.ts +3 -0
  625. package/dist/index-dashboard.d.ts.map +1 -0
  626. package/dist/index-dashboard.js +94 -0
  627. package/dist/index-dashboard.js.map +1 -0
  628. package/dist/platform/team-agents-client.d.ts +199 -0
  629. package/dist/platform/team-agents-client.d.ts.map +1 -0
  630. package/dist/platform/team-agents-client.js +263 -0
  631. package/dist/platform/team-agents-client.js.map +1 -0
  632. package/dist/platform/tunnel-client.d.ts +6 -0
  633. package/dist/platform/tunnel-client.d.ts.map +1 -1
  634. package/dist/platform/tunnel-client.js.map +1 -1
  635. package/dist/services/async-trigger-state.d.ts +7 -3
  636. package/dist/services/async-trigger-state.d.ts.map +1 -1
  637. package/dist/services/async-trigger-state.js +28 -0
  638. package/dist/services/async-trigger-state.js.map +1 -1
  639. package/dist/services/async-trigger-store.d.ts +13 -4
  640. package/dist/services/async-trigger-store.d.ts.map +1 -1
  641. package/dist/services/async-trigger-store.js +55 -8
  642. package/dist/services/async-trigger-store.js.map +1 -1
  643. package/dist/services/bot-config-store.d.ts +1 -11
  644. package/dist/services/bot-config-store.d.ts.map +1 -1
  645. package/dist/services/bot-config-store.js +27 -3
  646. package/dist/services/bot-config-store.js.map +1 -1
  647. package/dist/services/bot-profile-store.d.ts +17 -2
  648. package/dist/services/bot-profile-store.d.ts.map +1 -1
  649. package/dist/services/bot-profile-store.js +93 -12
  650. package/dist/services/bot-profile-store.js.map +1 -1
  651. package/dist/services/bridge-fallback-gate.d.ts +15 -7
  652. package/dist/services/bridge-fallback-gate.d.ts.map +1 -1
  653. package/dist/services/bridge-fallback-gate.js +43 -16
  654. package/dist/services/bridge-fallback-gate.js.map +1 -1
  655. package/dist/services/bridge-turn-queue.d.ts +10 -1
  656. package/dist/services/bridge-turn-queue.d.ts.map +1 -1
  657. package/dist/services/bridge-turn-queue.js +29 -9
  658. package/dist/services/bridge-turn-queue.js.map +1 -1
  659. package/dist/services/claude-transcript.d.ts +19 -0
  660. package/dist/services/claude-transcript.d.ts.map +1 -1
  661. package/dist/services/claude-transcript.js +57 -5
  662. package/dist/services/claude-transcript.js.map +1 -1
  663. package/dist/services/codex-app-threads.d.ts +2 -0
  664. package/dist/services/codex-app-threads.d.ts.map +1 -1
  665. package/dist/services/codex-app-threads.js +1 -1
  666. package/dist/services/codex-app-threads.js.map +1 -1
  667. package/dist/services/codex-reasoning-effort.d.ts +8 -0
  668. package/dist/services/codex-reasoning-effort.d.ts.map +1 -1
  669. package/dist/services/codex-reasoning-effort.js +24 -0
  670. package/dist/services/codex-reasoning-effort.js.map +1 -1
  671. package/dist/services/cursor-resume-policy.d.ts.map +1 -1
  672. package/dist/services/cursor-resume-policy.js +15 -3
  673. package/dist/services/cursor-resume-policy.js.map +1 -1
  674. package/dist/services/open-platform-rename.d.ts.map +1 -1
  675. package/dist/services/open-platform-rename.js +3 -2
  676. package/dist/services/open-platform-rename.js.map +1 -1
  677. package/dist/services/ordinary-turn-recovery.d.ts +76 -0
  678. package/dist/services/ordinary-turn-recovery.d.ts.map +1 -0
  679. package/dist/services/ordinary-turn-recovery.js +349 -0
  680. package/dist/services/ordinary-turn-recovery.js.map +1 -0
  681. package/dist/services/peer-cross-ref-store.d.ts +8 -0
  682. package/dist/services/peer-cross-ref-store.d.ts.map +1 -0
  683. package/dist/services/peer-cross-ref-store.js +95 -0
  684. package/dist/services/peer-cross-ref-store.js.map +1 -0
  685. package/dist/services/pi-transcript.d.ts.map +1 -1
  686. package/dist/services/pi-transcript.js +10 -6
  687. package/dist/services/pi-transcript.js.map +1 -1
  688. package/dist/services/resume-fresh-policy.d.ts +17 -0
  689. package/dist/services/resume-fresh-policy.d.ts.map +1 -0
  690. package/dist/services/resume-fresh-policy.js +24 -0
  691. package/dist/services/resume-fresh-policy.js.map +1 -0
  692. package/dist/services/session-group-title.d.ts +1 -0
  693. package/dist/services/session-group-title.d.ts.map +1 -1
  694. package/dist/services/session-group-title.js +16 -1
  695. package/dist/services/session-group-title.js.map +1 -1
  696. package/dist/services/session-groups-store.d.ts +30 -0
  697. package/dist/services/session-groups-store.d.ts.map +1 -1
  698. package/dist/services/session-groups-store.js +26 -4
  699. package/dist/services/session-groups-store.js.map +1 -1
  700. package/dist/services/session-owner-notification.d.ts +9 -0
  701. package/dist/services/session-owner-notification.d.ts.map +1 -0
  702. package/dist/services/session-owner-notification.js +10 -0
  703. package/dist/services/session-owner-notification.js.map +1 -0
  704. package/dist/services/session-owner-reminder-config-store.d.ts +9 -0
  705. package/dist/services/session-owner-reminder-config-store.d.ts.map +1 -0
  706. package/dist/services/session-owner-reminder-config-store.js +24 -0
  707. package/dist/services/session-owner-reminder-config-store.js.map +1 -0
  708. package/dist/services/session-owner-reminder-store.d.ts +5 -0
  709. package/dist/services/session-owner-reminder-store.d.ts.map +1 -0
  710. package/dist/services/session-owner-reminder-store.js +54 -0
  711. package/dist/services/session-owner-reminder-store.js.map +1 -0
  712. package/dist/services/session-store.d.ts +98 -21
  713. package/dist/services/session-store.d.ts.map +1 -1
  714. package/dist/services/session-store.js +406 -48
  715. package/dist/services/session-store.js.map +1 -1
  716. package/dist/services/structured-bridge-clis.d.ts +15 -2
  717. package/dist/services/structured-bridge-clis.d.ts.map +1 -1
  718. package/dist/services/structured-bridge-clis.js +16 -1
  719. package/dist/services/structured-bridge-clis.js.map +1 -1
  720. package/dist/services/transcript-resolver.d.ts +1 -1
  721. package/dist/services/transcript-resolver.d.ts.map +1 -1
  722. package/dist/services/transcript-resolver.js +6 -1
  723. package/dist/services/transcript-resolver.js.map +1 -1
  724. package/dist/setup/bot-config-editor.d.ts.map +1 -1
  725. package/dist/setup/bot-config-editor.js +7 -2
  726. package/dist/setup/bot-config-editor.js.map +1 -1
  727. package/dist/setup/ensure-herdr-integrations.d.ts +13 -0
  728. package/dist/setup/ensure-herdr-integrations.d.ts.map +1 -1
  729. package/dist/setup/ensure-herdr-integrations.js +21 -3
  730. package/dist/setup/ensure-herdr-integrations.js.map +1 -1
  731. package/dist/setup/open-platform-automation.d.ts +1 -0
  732. package/dist/setup/open-platform-automation.d.ts.map +1 -1
  733. package/dist/setup/open-platform-automation.js +107 -7
  734. package/dist/setup/open-platform-automation.js.map +1 -1
  735. package/dist/skills/definitions.d.ts +17 -0
  736. package/dist/skills/definitions.d.ts.map +1 -1
  737. package/dist/skills/definitions.js +78 -8
  738. package/dist/skills/definitions.js.map +1 -1
  739. package/dist/skills/injection-mode.d.ts +24 -3
  740. package/dist/skills/injection-mode.d.ts.map +1 -1
  741. package/dist/skills/injection-mode.js +64 -15
  742. package/dist/skills/injection-mode.js.map +1 -1
  743. package/dist/skills/installer.d.ts +18 -0
  744. package/dist/skills/installer.d.ts.map +1 -1
  745. package/dist/skills/installer.js +45 -1
  746. package/dist/skills/installer.js.map +1 -1
  747. package/dist/types.d.ts +222 -10
  748. package/dist/types.d.ts.map +1 -1
  749. package/dist/utils/bot-routing.d.ts +1 -1
  750. package/dist/utils/bot-routing.d.ts.map +1 -1
  751. package/dist/utils/bot-routing.js.map +1 -1
  752. package/dist/utils/child-env.d.ts +92 -2
  753. package/dist/utils/child-env.d.ts.map +1 -1
  754. package/dist/utils/child-env.js +195 -1
  755. package/dist/utils/child-env.js.map +1 -1
  756. package/dist/utils/cli-usage-limit.d.ts +25 -14
  757. package/dist/utils/cli-usage-limit.d.ts.map +1 -1
  758. package/dist/utils/cli-usage-limit.js +25 -15
  759. package/dist/utils/cli-usage-limit.js.map +1 -1
  760. package/dist/utils/daemon-rejection-guard.d.ts +35 -0
  761. package/dist/utils/daemon-rejection-guard.d.ts.map +1 -0
  762. package/dist/utils/daemon-rejection-guard.js +101 -0
  763. package/dist/utils/daemon-rejection-guard.js.map +1 -0
  764. package/dist/utils/dashboard-env.d.ts +53 -0
  765. package/dist/utils/dashboard-env.d.ts.map +1 -0
  766. package/dist/utils/dashboard-env.js +116 -0
  767. package/dist/utils/dashboard-env.js.map +1 -0
  768. package/dist/utils/global-install.d.ts.map +1 -1
  769. package/dist/utils/global-install.js +61 -5
  770. package/dist/utils/global-install.js.map +1 -1
  771. package/dist/utils/install-diagnostics.d.ts +14 -7
  772. package/dist/utils/install-diagnostics.d.ts.map +1 -1
  773. package/dist/utils/install-diagnostics.js +20 -11
  774. package/dist/utils/install-diagnostics.js.map +1 -1
  775. package/dist/utils/local-dev-update.d.ts +132 -0
  776. package/dist/utils/local-dev-update.d.ts.map +1 -0
  777. package/dist/utils/local-dev-update.js +176 -0
  778. package/dist/utils/local-dev-update.js.map +1 -0
  779. package/dist/utils/pending-input-queue.d.ts +12 -1
  780. package/dist/utils/pending-input-queue.d.ts.map +1 -1
  781. package/dist/utils/pending-input-queue.js.map +1 -1
  782. package/dist/worker.js +1517 -236
  783. package/dist/worker.js.map +1 -1
  784. package/dist/workflows/definition.d.ts +68 -68
  785. package/dist/workflows/events/payloads.d.ts +14 -14
  786. package/dist/workflows/events/schema.d.ts +312 -312
  787. package/dist/workflows/migration/v2-run-archive-schema.d.ts +40 -40
  788. package/dist/workflows/v3/cli-run.d.ts.map +1 -1
  789. package/dist/workflows/v3/cli-run.js +7 -0
  790. package/dist/workflows/v3/cli-run.js.map +1 -1
  791. package/dist/workflows/v3/goal-cli.d.ts +3 -0
  792. package/dist/workflows/v3/goal-cli.d.ts.map +1 -1
  793. package/dist/workflows/v3/goal-cli.js +11 -0
  794. package/dist/workflows/v3/goal-cli.js.map +1 -1
  795. package/dist/workflows/v3/run-envelope.d.ts +144 -144
  796. package/package.json +7 -2
  797. package/dist/cli/pm2-god-admission.d.ts +0 -8
  798. package/dist/cli/pm2-god-admission.d.ts.map +0 -1
  799. package/dist/cli/pm2-god-admission.js +0 -25
  800. package/dist/cli/pm2-god-admission.js.map +0 -1
  801. package/dist/core/riff-shutdown-detach.d.ts.map +0 -1
  802. package/dist/core/riff-shutdown-detach.js.map +0 -1
  803. package/dist/core/riff-worker-shutdown-readiness.d.ts.map +0 -1
  804. package/dist/core/riff-worker-shutdown-readiness.js.map +0 -1
  805. package/dist/dashboard-web/chunks/bot-defaults-page-4RENHE7J.js +0 -10
  806. package/dist/dashboard-web/chunks/chunk-2WQDS65D.js +0 -1
  807. package/dist/dashboard-web/chunks/chunk-G5XGBCM6.js +0 -1
  808. package/dist/dashboard-web/chunks/chunk-WLIVBPJC.js +0 -1
  809. package/dist/dashboard-web/chunks/groups-page-HLASRTSY.js +0 -8
  810. package/dist/dashboard-web/chunks/overview-page-6OK63V4V.js +0 -1
  811. package/dist/dashboard-web/chunks/sessions-page-B2TXGJDO.js +0 -8
  812. package/dist/dashboard-web/chunks/settings-page-6KJADSC6.js +0 -2
package/dist/daemon.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { execFileSync } from 'node:child_process';
2
2
  import { createHash, randomUUID } from 'node:crypto';
3
3
  import { readFileSync, existsSync, mkdirSync, unlinkSync } from 'node:fs';
4
+ import { installDaemonRejectionGuard } from './utils/daemon-rejection-guard.js';
4
5
  import { atomicWriteFileSync } from './utils/atomic-write.js';
6
+ import { readPeerCrossRef } from './services/peer-cross-ref-store.js';
5
7
  import { readAllowedUsersResolveCache, writeAllowedUsersResolveCache } from './utils/allowed-users-cache.js';
6
8
  import { join, dirname } from 'node:path';
7
9
  import { homedir, loadavg, cpus, totalmem, freemem } from 'node:os';
@@ -9,7 +11,7 @@ import { fileURLToPath } from 'node:url';
9
11
  const __filename = fileURLToPath(import.meta.url);
10
12
  const __dirname = dirname(__filename);
11
13
  import { config, getDashboardExternalHost, isVcMeetingAgentGloballyEnabled, vcMeetingAgentGlobalListenerBotAppId, } from './config.js';
12
- import { readGlobalConfig, repoPickerScanOptions } from './global-config.js';
14
+ import { readGlobalConfig, repoPickerScanOptions, isWorkflowFeatureEnabled } from './global-config.js';
13
15
  import { buildDashboardUrls } from './core/dashboard-url.js';
14
16
  import { resolveBotmuxDataDir } from './core/data-dir.js';
15
17
  import { reloadExactDaemonBotConfig } from './core/daemon-config-fence.js';
@@ -63,6 +65,15 @@ import { scrubTmuxServerGlobalEnv } from './setup/ensure-tmux.js';
63
65
  import { entryNeedsContactResolve } from './setup/bot-config-editor.js';
64
66
  import { invalidWorkingDirs } from './utils/working-dir.js';
65
67
  import { validateWorkingDir } from './core/working-dir.js';
68
+ function trustedCallerForTurn(larkAppId, senderOpenId, senderUnionId) {
69
+ if (!senderOpenId && !senderUnionId)
70
+ return undefined;
71
+ return {
72
+ ...(senderOpenId ? { requestUserOpenId: senderOpenId } : {}),
73
+ ...(senderUnionId ? { requestUserUnionId: senderUnionId } : {}),
74
+ requestLarkAppId: larkAppId,
75
+ };
76
+ }
66
77
  import { activeSessionKey, sessionKey, sessionAnchorId, storedSessionAnchorId, larkTransportEnabled, } from './core/types.js';
67
78
  import { stagePendingRepoSetup, persistPendingRepoCardMessageId } from './core/pending-repo-journal.js';
68
79
  import { setTerminalProxyPort, setTerminalExternalPort } from './core/terminal-url.js';
@@ -77,10 +88,11 @@ import { isLocalCliOpenReady } from './services/local-cli-opener.js';
77
88
  import { RECEIVED_REACTION_EMOJI_TYPE, SUBSTITUTE_RECEIVED_REACTION_EMOJI_TYPE } from './core/pending-response.js';
78
89
  import { t as tr, localeForBot } from './i18n/index.js';
79
90
  import { createCliAdapterSync } from './adapters/cli/registry.js';
80
- import { initWorkerPool, setActiveSessionsRegistry, forkWorker, forkAdoptWorker, sendWorkerInput, promoteQueuedActivationTail, admitQueuedActivationTail, reserveQueuedActivationTailAdmission, codexAppCleanInputAcceptedForSession, hasQueuedActivationAdmissionGate, sendWorkerSessionInput, killWorker, reapOrphanWorkers, scheduleCardPatch, setCurrentCliVersion, getCurrentCliVersion, CARD_POSTING_SENTINEL, parkStreamCard, closeSession as closeSessionHelper, setActiveSessionIfActive, rollbackRejectedSessionAndGetWinner, ensureCliEnv, sweepGlobalBotmuxSkills, writableTerminalLinkFor, workerHasInitialized, sessionSupportsWebTerminal, readableTerminalUrlFor, findActiveBySessionId, withActiveSessionKeyLock, getDaemonBootId, getDaemonStreamingCardUsageSnapshot, postTurnStartingCard, isSessionTransferring, snapshotCodexAppFinalSettlements, codexAppFinalSettlementCount, } from './core/worker-pool.js';
91
+ import { initWorkerPool, setActiveSessionsRegistry, forkWorker, forkAdoptWorker, sendWorkerInput, promoteQueuedActivationTail, admitQueuedActivationTail, reserveQueuedActivationTailAdmission, codexAppCleanInputAcceptedForSession, hasQueuedActivationAdmissionGate, sendWorkerSessionInput, killWorker, retireWorkerProcessOnly, retiringWorkersForSession, reapOrphanWorkers, scheduleCardPatch, setCurrentCliVersion, getCurrentCliVersion, CARD_POSTING_SENTINEL, parkStreamCard, closeSession as closeSessionHelper, closeSessionForBackgroundCleanup, setActiveSessionIfActive, rollbackRejectedSessionAndGetWinner, ensureCliEnv, sweepGlobalBotmuxSkills, writableTerminalLinkFor, workerHasInitialized, sessionSupportsWebTerminal, readableTerminalUrlFor, findActiveBySessionId, withActiveSessionKeyLock, getDaemonBootId, getDaemonStreamingCardUsageSnapshot, postTurnStartingCard, isSessionTransferring, snapshotCodexAppFinalSettlements, codexAppFinalSettlementCount, migrateMojoSessionIdentities, mojoLivePatchForSession, } from './core/worker-pool.js';
81
92
  import { waitAllWithin, trackProducerQuiet, trackProcessExited } from './core/producer-quiescence.js';
82
93
  import { AbortDeadlineError, hasExactSafeJsonKeys, ipcRoute, isTrustedHostIpcRequest, JsonBodyTooLargeError, jsonRes, readJsonBody, runWithAbortDeadline, setBotName, setLarkAppId, startIpcServer, setBotRenamer, setBotAvatarChanger, armCoreOnlyReadinessGate, setCoreOnlyReady, setSupervisorShutdownHandler } from './core/dashboard-ipc-server.js';
83
94
  import { setDeviceIsolationDaemonIdentity } from './core/device-isolation-daemon.js';
95
+ import { reconcileContainmentHandlesOnBoot } from './core/mojo-containment.js';
84
96
  import { cancelSessionReadyAck, waitForSessionReadyAck, } from './core/session-ready-handshake.js';
85
97
  import { loadOrCreateDashboardSecret, loadPersistedToken } from './dashboard/auth.js';
86
98
  import { daemonIpcAuthHeaders, fetchDaemonIpc, loadDaemonIpcSecret } from './core/daemon-ipc-auth.js';
@@ -98,6 +110,7 @@ import { consumeQuota, removeChatGrant, removeGlobalGrant } from './services/gra
98
110
  import { abortCharge, commitCharge, beginCharge } from './services/quota-dedup.js';
99
111
  import { getSessionWorkingDir, getProjectScanDirs, getProjectScanDirsForBot, expandHome, downloadResources, formatAttachmentsHint, buildNewTopicCliInput, buildFollowUpCliInput, buildBridgeInputContent, buildReforkCliInput, getAvailableBots, restoreActiveSessions, executeScheduledTask, persistStreamCardState, rememberLastCliInput, ensureTerminalWorkerPort, ensureSessionWhiteboard, resumeSession, closeCliMismatchedSessionsForBot, } from './core/session-manager.js';
100
112
  import { triggerSessionTurn, reconcileIdempotencyLeasesOnBoot, convergeIdempotentAsyncTurnOnWorkerExit, externalEventOpensOwnTopic } from './core/trigger-session.js';
113
+ import { runIdempotencyFailClose, runWithdrawAutoClose, } from './core/daemon-background-close.js';
101
114
  import { runDetachedBotTurnMutation, tryWithBotTurnMutation, withBotTurnAdmission, withBotTurnMutation, } from './core/bot-turn-mutation-gate.js';
102
115
  import { claimInitialUserTurn, isInitialUserTurnPending, releaseInitialUserTurn } from './core/initial-user-turn.js';
103
116
  import { applyQueuedCodexAppLegacyFallback, mergeQueuedCodexAppTurn } from './core/session-create.js';
@@ -113,11 +126,14 @@ import { HerdrBackend } from './adapters/backend/herdr-backend.js';
113
126
  import { ZellijBackend } from './adapters/backend/zellij-backend.js';
114
127
  import { ZmxBackend } from './adapters/backend/zmx-backend.js';
115
128
  import { sweepIdleWorkersAfterTurnDrain, DEFAULT_MAX_LIVE_WORKERS } from './core/idle-worker-sweeper.js';
116
- import { getSessionPersistentBackendType, killPersistentBackendTarget, killPersistentSession, probePersistentBackendTarget, probePersistentSession, resolvePersistentBackendTarget, resolvePairedSpawnBackendType, } from './core/persistent-backend.js';
129
+ import { DEFAULT_SESSION_OWNER_REMINDER, SessionOwnerReminderController, } from './core/session-owner-reminder.js';
130
+ import { loadSessionOwnerReminderRecords, saveSessionOwnerReminderRecords, } from './services/session-owner-reminder-store.js';
131
+ import { sendSessionOwnerThreadNotification } from './services/session-owner-notification.js';
132
+ import { getSessionPersistentBackendType, isRemoteBackendSession, killPersistentBackendTarget, killPersistentSession, probePersistentBackendTarget, probePersistentSession, resolvePersistentBackendTarget, resolvePairedSpawnBackendType, } from './core/persistent-backend.js';
117
133
  import { handleCardAction, runAutoWorktreeCommit } from './im/lark/card-handler.js';
118
134
  import { setIssueActivate } from './im/lark/issue-command-deps.js';
119
135
  import { startIssueOutboxPump } from './services/issue-outbox-pump.js';
120
- import { parseWorkflowGrillTrigger, buildWorkflowGrillPrompt, isLegacyTemplateCommand, LEGACY_TEMPLATE_RETIRED_MESSAGE, WORKFLOW_USAGE, } from './im/lark/workflow-slash-command.js';
136
+ import { parseWorkflowGrillTrigger, buildWorkflowGrillPrompt, isLegacyTemplateCommand, LEGACY_TEMPLATE_RETIRED_MESSAGE, WORKFLOW_USAGE, WORKFLOW_DISABLED_MESSAGE, } from './im/lark/workflow-slash-command.js';
121
137
  import { parseV3SavedWorkflowCommand, v3SavedWorkflowUsage, } from './im/lark/v3-saved-workflow-command.js';
122
138
  import { authorizeV3SavedWorkflowInvocation, defaultV3SavedWorkflowExecutionServices, deliverV3SavedWorkflowNotification, executeV3SavedWorkflowCommand, resolveV3SavedWorkflowMessageTargets, } from './im/lark/v3-saved-workflow-handler.js';
123
139
  import { createV3GateRunner, preflightV3RunStart, readV3RunChatBinding, requestV3RunCancel, requestV3Retry, requestV3LoopGrant, } from './workflows/v3/daemon-run.js';
@@ -172,7 +188,7 @@ function republishResolvedAllowedUsers(larkAppId, resolved) {
172
188
  catch { /* best effort */ }
173
189
  }
174
190
  let vcMeetingTerminalReconciler;
175
- import { isBotMentioned, probeBotOpenId, startLarkEventDispatcher, markForwardFollowupsSessionsReady, writeBotInfoFile, canOperate, canRunDaemonCommand, evaluateTalk, evaluateBotTalk, evaluateAskAnswerTalk, grantCommandRestriction, isKnownPeerBot, checkRequiredScopes } from './im/lark/event-dispatcher.js';
191
+ import { isBotMentioned, probeBotOpenId, startLarkEventDispatcher, markForwardFollowupsSessionsReady, writeBotInfoFile, canOperate, canRunDaemonCommand, evaluateTalk, evaluateBotTalk, evaluateAskAnswerTalk, askCustomReplyCandidate, grantCommandRestriction, isKnownPeerBot, checkRequiredScopes } from './im/lark/event-dispatcher.js';
176
192
  import { getDocSubscription, listAllDocSubscriptions, putDocSubscription, removeDocSubscription, setDocCommentPollCursor } from './services/doc-subs-store.js';
177
193
  import { BOT_REPLY_SENTINEL, subscribeDocFile, unsubscribeDocFile, addCommentReaction, removeCommentReaction, hasBotSentinel, isBotAuthoredReply, listDocComments } from './im/lark/doc-comment.js';
178
194
  import { learnFromMentions, resolveSender, flushIdentityCacheSync } from './im/lark/identity-cache.js';
@@ -181,7 +197,7 @@ import { buildDocCommentTurnInput, buildDocWatchWarmupTurnInput } from './core/d
181
197
  import { advanceDocCommentCursor, docCommentRepliesAfterCursor, latestDocCommentPollCursor } from './core/doc-comment-poller.js';
182
198
  import { renderBufferedSenderBlock } from './core/session-manager.js';
183
199
  import { shutdownBackendDisposition } from './core/persistent-backend.js';
184
- import { abortRiffShutdownFleet, canAbortVerifiedExitedRiffPreparation, collectUniqueDaemonShutdownSessions, commitPreparedRiffShutdown, isPreparedRiffSessionCurrent, persistPreparedRiffShutdownFleet, prepareRiffFleetForShutdown, } from './core/riff-shutdown-detach.js';
200
+ import { abortRemoteShutdownFleet, canAbortVerifiedExitedRemotePreparation, collectUniqueDaemonShutdownSessions, commitPreparedRemoteShutdown, isPreparedRemoteSessionCurrent, persistPreparedRemoteShutdownFleet, prepareRemoteFleetForShutdown, } from './core/remote-shutdown-detach.js';
185
201
  import { BOT_TURN_MUTATION_SHUTDOWN_ACQUIRE_TIMEOUT_MS, DAEMON_SHUTDOWN_MAX_MS, DAEMON_WORKER_EXIT_GRACE_MS, } from './core/shutdown-budgets.js';
186
202
  import { evaluateVcMeetingConsumerIsolation } from './services/vc-meeting-consumer-isolation.js';
187
203
  import { markSessionActivity, announcePendingRepoSession, publishAttentionPatch, publishLastInputFromBotPatch, publishSessionMessagePreviewPatch, publishClosedSessionPatch, clearAgentAttention, } from './core/session-activity.js';
@@ -201,7 +217,7 @@ import { connectRealtimeVoiceTransport, createProtoRealtimeVoiceProtocol, fetchR
201
217
  import { createGroupWithBots } from './services/group-creator.js';
202
218
  import { addBotToChat, isInChat } from './services/groups-store.js';
203
219
  import { initSessionGroups, isSessionGroup, getSessionGroup, touchSessionGroup } from './services/session-groups-store.js';
204
- import { maybeBirthSessionGroup } from './core/session-group-birth.js';
220
+ import { maybeBirthSessionGroup, declinesSessionGroupBirthAsSlashCommand } from './core/session-group-birth.js';
205
221
  import { scheduleSessionGroupTitle } from './services/session-group-title.js';
206
222
  import { setChatReplyMode } from './services/chat-reply-mode-store.js';
207
223
  import { hasVcMeetingEndedTombstone, listVcMeetingRuntimeSessionsByListenerAndAgent, listVcMeetingRuntimeSessions, pruneExpiredVcMeetingRuntimeSessions, recordVcMeetingEndedTombstone, recordVcMeetingRuntimeSession, removeVcMeetingRuntimeSession, } from './services/vc-meeting-runtime-store.js';
@@ -230,6 +246,9 @@ const activeSessions = new Map();
230
246
  * (codex P1-2). While false, /api/asks returns a retryable 503 for unknown
231
247
  * sessions instead, so the reconnecting hook keeps waiting through the restore. */
232
248
  let sessionsRestored = false;
249
+ /** Once-per-daemon guard for the mojo containment boot reconciliation. The store
250
+ * is bot-agnostic, so it must run once regardless of how many bots start. */
251
+ let mojoContainmentReconciledThisBoot = false;
233
252
  const VC_MEETING_DELIVERY_LEASE_MS = 15 * 60_000;
234
253
  const VC_MEETING_DELIVERY_LEASE_SCAN_MS = 60_000;
235
254
  const VC_MEETING_RUNTIME_EXPIRY_ACK_TIMEOUT_MS = 3_000;
@@ -366,15 +385,37 @@ function vcMeetingPersistentBackendAvailable(backendType) {
366
385
  return ZellijBackend.isAvailable();
367
386
  return ZmxBackend.isAvailable();
368
387
  }
388
+ /**
389
+ * VC receiver teardown: this fence's job is to guarantee the local PRODUCER
390
+ * process is dead before hub replay is admitted. killWorker refuses an
391
+ * unprepared live remote worker (P0-2) — the fail-safe direction for lineage,
392
+ * but for THIS caller a silent refusal is fail-OPEN: the pane probes then read
393
+ * `missing` (mojo has no persistent pane) and the fence cleared while the
394
+ * producer kept running. Remote-frozen receivers are therefore retired
395
+ * process-only — no close IPC, no remote cancel, lineage and containment
396
+ * handles intact — with retireWorkerProcessOnly's SIGTERM→SIGKILL backstop
397
+ * guaranteeing death; local receivers keep killWorker so their backing
398
+ * session is torn down as before.
399
+ */
400
+ function teardownVcReceiverWorker(ds) {
401
+ if (isRemoteBackendSession(ds))
402
+ retireWorkerProcessOnly(ds, 'vc_runtime_lease');
403
+ else
404
+ killWorker(ds);
405
+ }
369
406
  let testOnlyVcMeetingBootBackingMissing;
370
407
  function vcMeetingBootBackingMissing(sessionId, destroy) {
371
408
  if (testOnlyVcMeetingBootBackingMissing) {
372
409
  return testOnlyVcMeetingBootBackingMissing(sessionId, destroy);
373
410
  }
374
411
  const ds = findActiveBySessionId(sessionId);
412
+ // Captured BEFORE teardown: retirement nulls ds.worker while the process
413
+ // dies asynchronously, and the producer-liveness check below needs the real
414
+ // ChildProcess to prove the exit.
415
+ const workerBefore = ds?.worker && !ds.worker.killed ? ds.worker : null;
375
416
  if (destroy && ds) {
376
417
  try {
377
- killWorker(ds);
418
+ teardownVcReceiverWorker(ds);
378
419
  }
379
420
  catch (err) {
380
421
  logger.warn(`[vc-delivery] boot recovery worker teardown failed session=${sessionId}: ${err}`);
@@ -415,6 +456,16 @@ function vcMeetingBootBackingMissing(sessionId, destroy) {
415
456
  + `backend=${backendType} session=${sessionId}`);
416
457
  }
417
458
  }
459
+ // The worker PROCESS is itself the producer this recovery gates on; pane
460
+ // probes say nothing about it (mojo has no pane at all, so they always read
461
+ // `missing`). Clear only once the previously-live worker has actually
462
+ // exited — the retirement's SIGTERM→SIGKILL backstop makes a later reprobe
463
+ // see it gone.
464
+ if (workerBefore && workerBefore.exitCode === null && workerBefore.signalCode === null) {
465
+ allMissing = false;
466
+ logger.error(`[vc-delivery] boot recovery worker process still live; receiver remains gated `
467
+ + `session=${sessionId}`);
468
+ }
418
469
  return allMissing;
419
470
  }
420
471
  function scheduleVcMeetingBootRecoveryProbe(key, sessionId, delayMs) {
@@ -448,9 +499,11 @@ function escalateVcMeetingBootRecovery(key, sessionId) {
448
499
  vcMeetingReceiverRecoveryEscalating.add(key);
449
500
  const ds = findActiveBySessionId(sessionId);
450
501
  if (ds)
451
- killWorker(ds);
452
- // Wait past killWorker's 7s SIGKILL backstop, then direct-kill and probe the
453
- // persisted exact backend. exists/unknown stays scoped-gated and reprobes.
502
+ teardownVcReceiverWorker(ds);
503
+ // Wait past the retirement's SIGTERM→SIGKILL backstop window (killWorker for
504
+ // local receivers, retireWorkerProcessOnly for remote-frozen ones — a plain
505
+ // killWorker would REFUSE a live remote worker and arm nothing), then
506
+ // direct-kill and probe. exists/unknown stays scoped-gated and reprobes.
454
507
  scheduleVcMeetingBootRecoveryProbe(key, sessionId, VC_MEETING_RUNTIME_EXPIRY_TEARDOWN_MS);
455
508
  }
456
509
  function acknowledgeVcMeetingReceiverRecovery(key) {
@@ -579,6 +632,34 @@ function createVcMeetingRuntimeLeaseRecovery(deps) {
579
632
  scheduleReprobe(fence, 'identity conflict');
580
633
  return false;
581
634
  }
635
+ // Captured BEFORE teardown: retirement nulls ds.worker while the process
636
+ // dies asynchronously; the producer-liveness gate below needs the real
637
+ // ChildProcess to prove the exit. Falls back to the process observed at
638
+ // arm time when the registry no longer resolves the ds — a deleted
639
+ // registry entry is not proof the producer died (fourth-round review).
640
+ const armedAlive = fence.armedWorker
641
+ && !fence.armedWorker.killed
642
+ && fence.armedWorker.exitCode === null
643
+ && fence.armedWorker.signalCode === null
644
+ ? fence.armedWorker : null;
645
+ // Third source, for a fence armed AFTER the registry delete (the other
646
+ // half of the collision-loser timing): a process-only retirement is still
647
+ // dying for this session id, and neither ds nor arm-time capture ever saw
648
+ // it. Pin it onto the fence so later reprobes keep tracking the same
649
+ // process.
650
+ let retiringAlive = null;
651
+ if (!armedAlive && !(ds?.worker && !ds.worker.killed)) {
652
+ for (const w of deps.resolveRetiringWorkers?.(fence.receiverSessionId) ?? []) {
653
+ if (w && !w.killed && w.exitCode === null && w.signalCode === null) {
654
+ retiringAlive = w;
655
+ fence.armedWorker = w;
656
+ break;
657
+ }
658
+ }
659
+ }
660
+ const workerBefore = (ds?.worker && !ds.worker.killed ? ds.worker : null)
661
+ ?? armedAlive
662
+ ?? retiringAlive;
582
663
  if (ds) {
583
664
  try {
584
665
  deps.killWorker(ds);
@@ -625,6 +706,17 @@ function createVcMeetingRuntimeLeaseRecovery(deps) {
625
706
  + `attempt=${fence.dispatchAttempt}`);
626
707
  }
627
708
  }
709
+ // The worker PROCESS is itself the producer this fence gates on; pane
710
+ // probes say nothing about it — mojo owns no pane, so they always read
711
+ // `missing`, which is exactly how a refused/incomplete teardown became
712
+ // fail-OPEN (third-round review, gate 3). Clear only once the previously
713
+ // live worker has actually EXITED; the retirement's SIGTERM→SIGKILL
714
+ // backstop guarantees a later reprobe sees it gone.
715
+ if (workerBefore && workerBefore.exitCode === null && workerBefore.signalCode === null) {
716
+ allMissing = false;
717
+ deps.error(`[vc-delivery] runtime lease worker process still live; delivery remains gated `
718
+ + `session=${fence.receiverSessionId} attempt=${fence.dispatchAttempt}`);
719
+ }
628
720
  // PTY has no surviving backing process once its worker has crossed the
629
721
  // SIGKILL backstop. Persistent backends unlock only after every applicable
630
722
  // deterministic name probes authoritatively missing. The backing proof is
@@ -679,8 +771,11 @@ function createVcMeetingRuntimeLeaseRecovery(deps) {
679
771
  deps.error(`[vc-delivery] runtime lease escalation failed to kill worker `
680
772
  + `session=${fence.receiverSessionId}: ${err instanceof Error ? err.message : String(err)}`);
681
773
  }
682
- // killWorker's live path has a 7s SIGKILL backstop. Only after that window
683
- // do we directly kill + probe the deterministic owned pane.
774
+ // The teardown above arms a SIGTERM→SIGKILL backstop (killWorker's close
775
+ // path for local receivers; retireWorkerProcessOnly for remote-frozen
776
+ // ones — a plain killWorker would REFUSE a live remote worker and arm
777
+ // nothing, which is why the fence used to fail open for mojo). Only after
778
+ // that window do we directly kill + probe the deterministic owned pane.
684
779
  fence.timer = setTimeout(() => {
685
780
  if (!isCurrent(fence) || fence.phase !== 'escalating')
686
781
  return;
@@ -717,6 +812,10 @@ function createVcMeetingRuntimeLeaseRecovery(deps) {
717
812
  teardownAndProbe(fence, !ds ? 'missing-session teardown' : 'workerless teardown');
718
813
  return;
719
814
  }
815
+ // Remember the live producer process itself: registry state can stop
816
+ // resolving this ds before the process has died, and the liveness gate in
817
+ // teardownAndProbe must keep seeing it.
818
+ fence.armedWorker = ds.worker;
720
819
  if ((ds.workerGeneration ?? 0) !== ref.workerGeneration) {
721
820
  deps.error(`[vc-delivery] runtime lease worker generation mismatch; escalating `
722
821
  + `session=${ref.receiverSessionId} receipt=${ref.workerGeneration} `
@@ -765,6 +864,12 @@ function createVcMeetingRuntimeLeaseRecovery(deps) {
765
864
  return false;
766
865
  }
767
866
  if (context.disposition === 'queued_removed') {
867
+ // No producer-exit proof required here, deliberately: `queued_removed`
868
+ // is an ACK from the EXACT expected worker generation saying it removed
869
+ // the queued delivery itself. The producer is alive, current and
870
+ // cooperating — the double-delivery risk this fence gates on is gone
871
+ // without anyone dying. Exit proof is demanded only on the teardown
872
+ // paths, where the producer's state is unknown or adversarial.
768
873
  clearFence(fence);
769
874
  return true;
770
875
  }
@@ -836,7 +941,8 @@ const vcMeetingRuntimeLeaseRecovery = createVcMeetingRuntimeLeaseRecovery({
836
941
  throw new Error('receiver worker is not live');
837
942
  ds.worker.send(message);
838
943
  },
839
- killWorker,
944
+ killWorker: teardownVcReceiverWorker,
945
+ resolveRetiringWorkers: retiringWorkersForSession,
840
946
  resolvePersistentScope(ds) {
841
947
  const backendType = getSessionPersistentBackendType(ds);
842
948
  if (backendType)
@@ -1942,19 +2048,34 @@ function scheduleDeferredScheduleSettlement(ds, context) {
1942
2048
  // Exact transcript terminal needs only a short filesystem visibility grace.
1943
2049
  // Screen-only adapters use a longer confirmation and re-check that the same
1944
2050
  // turn is still idle before reclaiming the hidden session.
2051
+ // Remote backends (riff / mojo) report their turn boundary over the network,
2052
+ // so allow the same longer grace as the screen-only path instead of the 300ms
2053
+ // local-filesystem one.
2054
+ const remoteTerminal = ds.session.backendType === 'riff'
2055
+ || ds.session.backendType === 'mojo';
1945
2056
  const delayMs = context.source === 'terminal'
1946
- ? (ds.session.backendType === 'riff' ? 1_500 : 300)
2057
+ ? (remoteTerminal ? 1_500 : 300)
1947
2058
  : 1_500;
1948
2059
  const timer = setTimeout(() => {
1949
2060
  deferredScheduleSettleTimers.delete(sessionId);
1950
2061
  void settleDeferredScheduleRun(ds, context, {
1951
2062
  reconcile: reconcileDeferredTopicBinding,
1952
- closeSession: closeSessionHelper,
2063
+ // Background path: no user surface, so a residual/refusal must at least be
2064
+ // logged with its remote id instead of being discarded.
2065
+ closeSession: (id) => closeSessionForBackgroundCleanup(id, 'deferred-schedule settlement'),
1953
2066
  }).then((result) => {
1954
2067
  if (result.action === 'materialized') {
1955
2068
  logger.info(`[scheduler] Deferred topic materialized session=${sessionId.slice(0, 8)} root=${result.rootMessageId.slice(0, 12)}`);
1956
2069
  return;
1957
2070
  }
2071
+ if (result.action === 'close_refused') {
2072
+ // Must NOT print "Auto-closed": the row is still active and its remote
2073
+ // session may still be running. Two contradictory log lines about the same
2074
+ // session is exactly how a false success hides.
2075
+ logger.error(`[scheduler] Deferred run close REFUSED (${result.error ?? 'unknown'}); session stays `
2076
+ + `ACTIVE session=${sessionId.slice(0, 8)} turn=${context.turnId.slice(0, 12)} source=${context.source}`);
2077
+ return;
2078
+ }
1958
2079
  if (result.action === 'closed') {
1959
2080
  logger.info(`[scheduler] Auto-closed silent deferred run with no botmux send `
1960
2081
  + `session=${sessionId.slice(0, 8)} turn=${context.turnId.slice(0, 12)} source=${context.source}`);
@@ -2374,8 +2495,11 @@ async function maybeSeedCardlessForceTopicTurn(args) {
2374
2495
  }
2375
2496
  }
2376
2497
  async function revokeQuotaGrant(larkAppId, chatId, senderOpenId, ev) {
2498
+ // 会话群里的 chatGrant 是**继承**来的:授权记录挂在出生前那个私聊上(ev.grantChatId),
2499
+ // 不在会话群自己身上。撤销必须落到来源 chat,否则删了个不存在的记录,硬上限失效。
2500
+ const grantChatId = ev.grantChatId ?? chatId;
2377
2501
  const result = ev.reason === 'chatGrant'
2378
- ? await removeChatGrant(larkAppId, chatId, senderOpenId)
2502
+ ? await removeChatGrant(larkAppId, grantChatId, senderOpenId)
2379
2503
  : ev.reason === 'globalGrant'
2380
2504
  ? await removeGlobalGrant(larkAppId, senderOpenId)
2381
2505
  : { ok: true, removed: false };
@@ -2396,6 +2520,20 @@ async function notifyQuotaExhausted(larkAppId, anchor, senderOpenId, limit) {
2396
2520
  export async function enforceMessageQuotaForCliInput(larkAppId, chatId, senderOpenId, messageId, anchor, senderUnionId, memberUnionId, chatType, botSender, opts) {
2397
2521
  if (opts?.listenerAuthorized)
2398
2522
  return true;
2523
+ // 会话群出生轮:**同一条消息**的授权判定与扣费,刚刚由本函数在原 DM 上下文里
2524
+ // 一起做完(handleNewTopicAdmitted 的建群前扣费点:扣费被拒必须零外部副作用,
2525
+ // 所以它必须排在 createGroupWithBots 之前)。这一轮绝不能再复查一次:
2526
+ // • 新群是 bot 刚为这个发送者建的,chat 维度的 talk 来源(chatGrant /
2527
+ // allowedChatGroup / oncall / p2p 专属的 p2pOpen)按定义不可能已经落在它
2528
+ // 上面,复查只会产出假阴性;
2529
+ // • 而这条消息**已经扣过一次费**——被假阴性丢掉就成了「扣了费还把任务丢了」:
2530
+ // 用户额度少一格,任务无声消失,连一句拒绝提示都没有(丢弃分支只打 debug 日志)。
2531
+ // 「扣了费就不能丢任务,丢任务就不能扣费」是这道闸的硬不变量,两侧只能同进同退。
2532
+ // 授权本身并没有被放宽:真正的判定就是 DM 那次(真实 chatId + chatType='p2p'),
2533
+ // 这里只是不拿一个必然更严的新上下文去推翻它。跨 chat 的授权继承缺口是另一件事
2534
+ //(新群里的**后续**消息仍按新 chat 判定),不由这条已扣费的出生轮来承担。
2535
+ if (opts?.alreadyAuthorizedAndCharged)
2536
+ return true;
2399
2537
  // senderUnionId(bot-locked)让 evaluateTalk 认出跨部署团队 peer bot(teamBot 腿);
2400
2538
  // memberUnionId(可为真人 union)走 teamMember 腿——否则外部闸门/群闸门放进来的
2401
2539
  // 团队 bot 或团队成员消息会在这里复查处被静默丢弃(#332 端到端断点,人腿同理)。
@@ -2993,6 +3131,13 @@ async function handleV3SavedWorkflowCommandIfAny(invocation) {
2993
3131
  await notify('❌ Saved Workflow 需要可验证的飞书用户身份。', 'authorization');
2994
3132
  return true;
2995
3133
  }
3134
+ // Machine-wide workflow kill-switch: refuse starting/authoring a workflow when
3135
+ // the feature is off. `cancel` (and other decrease-only management) stays
3136
+ // available so an in-flight run started before the flip can be wound down.
3137
+ if ((command.kind === 'save' || command.kind === 'run') && !isWorkflowFeatureEnabled()) {
3138
+ await notify(WORKFLOW_DISABLED_MESSAGE, 'validation');
3139
+ return true;
3140
+ }
2996
3141
  if (
2997
3142
  // Cancellation is decrease-only and is still protected below by the
2998
3143
  // immutable owner/chat/app binding (or explicit canOperate). Keep it
@@ -3128,6 +3273,7 @@ function beginNewTurn(ds, title, turnId) {
3128
3273
  ds.parkedStreamCardNonce = ds.streamCardNonce;
3129
3274
  ds.frozenCards.set(ds.streamCardNonce, {
3130
3275
  messageId: ds.streamCardId,
3276
+ replyTargetKey: ds.streamCardReplyTargetKey,
3131
3277
  content: ds.lastScreenContent ?? '',
3132
3278
  title: prevTitle,
3133
3279
  displayMode: prevMode,
@@ -3685,10 +3831,16 @@ async function adoptCodexNotifierEvent(larkAppId, event, cardMessageId, ownerOpe
3685
3831
  }
3686
3832
  // 接管原生 Codex App 线程时固定为纯 codex-app 启动,不能继承通知 Bot 的
3687
3833
  // wrapper/model;这些配置针对普通 CLI,会错误包装 app-server runner。
3834
+ // model 由 resolveSessionLaunchModel 在每次 spawn 时解析:这里把 cliId 钉成
3835
+ // codex-app 后,若通知 Bot 自己不是 codex-app,session 与 bot 的 cliId 不匹配
3836
+ // → 不会拿 bot 那个面向别的 CLI 的 model;清掉历史记录值让兜底也为空。
3837
+ // spawnModelOverride(trigger 的一次性 model)优先级最高且无条件,必须一并清掉,
3838
+ // 否则它会泄漏进接管后的 codex-app 启动。
3688
3839
  ds.session.cliId = 'codex-app';
3689
3840
  ds.session.cliPathOverride = botCfg.cliPathOverride;
3690
3841
  delete ds.session.wrapperCli;
3691
3842
  delete ds.session.model;
3843
+ ds.spawnModelOverride = undefined;
3692
3844
  ds.session.agentFrozen = true;
3693
3845
  sessionStore.updateSession(ds.session);
3694
3846
  // 默认 flat DM(p2pMode=chat)下这条按钮回调复用同一私聊的现有会话。若该会话
@@ -3906,6 +4058,14 @@ workflowDaemonMutationRoute('start', async (reply, params, _body, identity) => {
3906
4058
  const runId = params.runId;
3907
4059
  if (!isValidV3RunId(runId))
3908
4060
  return reply(400, { ok: false, error: 'bad_run_id' });
4061
+ // Machine-wide workflow kill-switch: refuse launching a NEW daemon-driven run
4062
+ // when the feature is off. This is the common sink for both `botmux workflow
4063
+ // start` and the grill `approve-dag` kick, so gating here closes the edge where
4064
+ // a run was grilled+approved before the flip. Restart recovery of an
4065
+ // already-started run (coldAttach / reconcileV3PendingGates) is a separate path
4066
+ // and is intentionally NOT gated, and `retry`/`grant`/`cancel` stay available.
4067
+ if (!isWorkflowFeatureEnabled())
4068
+ return reply(409, { ok: false, error: 'workflow_disabled' });
3909
4069
  const runDir = join(v3DefaultBaseDir(), runId);
3910
4070
  const preflight = preflightV3RunStart(runDir);
3911
4071
  if (!preflight.ok) {
@@ -13696,12 +13856,14 @@ export const __testOnly_onQueuedActivationSubmitted = onQueuedActivationSubmitte
13696
13856
  * deliberately no await between the final buffered-input snapshot, fork, and
13697
13857
  * release: a later handler either buffers before this block or observes the
13698
13858
  * live worker after it. */
13699
- function forkReservedInitialSession(ds, availableBots) {
13859
+ function forkReservedInitialSession(ds, availableBots, trustedCaller) {
13700
13860
  const userPrompt = ds.pendingPrompt ?? '';
13701
13861
  const input = buildReservedInitialInput(ds, availableBots);
13862
+ if (trustedCaller)
13863
+ input.trustedCaller = trustedCaller;
13702
13864
  rememberLastCliInput(ds, userPrompt, input);
13703
13865
  const turnId = ds.pendingTurnId ?? ds.session.pendingRepoSetup?.turnId;
13704
- forkWorker(ds, input, turnId ? { turnId } : false);
13866
+ forkWorker(ds, input, turnId ? { turnId, trustedCaller } : false);
13705
13867
  ds.pendingTurnId = undefined;
13706
13868
  // A no-project pendingRepo fallback enters forkWorker with `session.queued`
13707
13869
  // still set. forkWorker materializes the exact opening as a tokened
@@ -13804,15 +13966,15 @@ function isInitialSessionPassthrough(larkAppId, cmd) {
13804
13966
  /** `/fast` is a passthrough keystroke to Codex's native tier toggle — it only
13805
13967
  * reaches the executor on a real paste TUI. In RPC input mode the pane is a
13806
13968
  * pure viewer (turns go over JSON-RPC, keystrokes never reach the app-server),
13807
- * and the Riff backend turns the text+Enter into two remote task writes. On
13808
- * those backends the toggle is a silent no-op (or worse), so fail closed:
13969
+ * and a remote backend (riff / mojo) turns the text+Enter into two independent
13970
+ * remote turns. On those backends the toggle is a silent no-op (or worse), so fail closed:
13809
13971
  * reject with a clear message instead of pretending it worked. The read-only
13810
13972
  * card badge is unaffected — it reflects whatever tier Codex actually runs. */
13811
13973
  function fastToggleUnsupportedBackend(ds) {
13812
13974
  if (!ds)
13813
13975
  return false;
13814
13976
  const backendType = ds.initConfig?.backendType ?? ds.session.backendType;
13815
- if (backendType === 'riff')
13977
+ if (backendType === 'riff' || backendType === 'mojo')
13816
13978
  return true;
13817
13979
  return ds.initConfig?.codexRpcInput === true;
13818
13980
  }
@@ -13887,6 +14049,9 @@ function deliverPassthroughToExistingSession(ds, cmd, commandContent, anchor, la
13887
14049
  sendWorkerSessionInput(ds, {
13888
14050
  type: 'raw_input',
13889
14051
  content: commandContent,
14052
+ // Same reason as the worker-pool raw_input send: a passthrough is a real
14053
+ // mojo turn, so it needs the live credential snapshot too.
14054
+ ...(mojoLivePatchForSession(ds) ?? {}),
13890
14055
  turnId: turn.messageId,
13891
14056
  });
13892
14057
  turn.onDelivered?.();
@@ -14151,6 +14316,7 @@ async function handleNewTopicAdmitted(data, ctx) {
14151
14316
  // routing into thread-scope so the bot's first reply seeds a Lark thread.
14152
14317
  let scope = ctx.scope;
14153
14318
  let anchor = ctx.anchor;
14319
+ let quotaConsumedBeforeSessionGroupBirth = false;
14154
14320
  // ─── p2pMode='group':会话群出生 ────────────────────────────────────────
14155
14321
  // group 模式下,顶层 DM 消息在任何会话机制启动前先改道:建一个专属会话群
14156
14322
  //(bot 保留群主),把本轮 context 重写到新群(chat-scope)后递归。返回 null
@@ -14166,10 +14332,32 @@ async function handleNewTopicAdmitted(data, ctx) {
14166
14332
  && !substituteTrigger
14167
14333
  && scope === 'thread'
14168
14334
  && anchor === messageId
14169
- && !data?.message?.thread_id) {
14335
+ // 「话题内发送 + 同时发送到会话」的消息 root_id 为空但带 thread_id——
14336
+ // 语境属于既有话题,绝不当全新会话建群(回退 thread 旧行为)。
14337
+ && !data?.message?.thread_id
14338
+ // 斜杠命令判定必须**早于扣费**:/help、/login、/close 本来就不进 CLI,既不该
14339
+ // 被预扣一次额度,也不该在额度耗尽时被这道闸静默丢掉(下面的 return 会让命令
14340
+ // 根本执行不到)。maybeBirthSessionGroup 对同一条消息本就返回 null(命令不建
14341
+ // 群),所以这里只是把它的结论提前一步,建群行为一字未改;命令随后照常走
14342
+ // parseSlashCommandInvocation 分支,真正会注入 CLI 的消息(含未注册的 /foo,
14343
+ // 它落不进命令分支)仍由下面 pre-worker 那道闸按原样扣一次费——净扣费不变。
14344
+ // 谓词刻意复用 birth 内部的同一个:两侧同源才能保证「跳过扣费」与「不建群」
14345
+ // 是同一个判断——窄于它会退回本 bug,宽于它则会让本该建群的消息不再建群。
14346
+ && !declinesSessionGroupBirthAsSlashCommand(data)) {
14347
+ // A quota denial must have zero external side effects. Charge the original
14348
+ // DM before creating the real Feishu chat, then mark the rewritten turn so
14349
+ // the ordinary pre-worker gate below does not charge it a second time.
14350
+ const quotaSenderOpenId = data.sender?.sender_id?.open_id;
14351
+ const quotaSenderUnionId = data.sender?.sender_id?.union_id;
14352
+ const quotaTeamTrustUnionId = (data.sender?.sender_type === 'app' || data.sender?.sender_type === 'bot')
14353
+ ? quotaSenderUnionId
14354
+ : undefined;
14355
+ if (!await enforceMessageQuotaForCliInput(larkAppId, chatId, quotaSenderOpenId, messageId, anchor, quotaTeamTrustUnionId, quotaSenderUnionId, chatType))
14356
+ return;
14357
+ quotaConsumedBeforeSessionGroupBirth = true;
14170
14358
  const reborn = await maybeBirthSessionGroup(data, ctx);
14171
14359
  if (reborn)
14172
- return handleNewTopic(data, reborn);
14360
+ return handleNewTopic(data, { ...reborn, sessionGroupQuotaConsumed: true });
14173
14361
  }
14174
14362
  // ─── 会话群同群续聊 ─────────────────────────────────────────────────────
14175
14363
  // 会话群里的新消息发现登记的会话已关闭时,自动 resume 原会话(与话题内续聊
@@ -14251,6 +14439,7 @@ async function handleNewTopicAdmitted(data, ctx) {
14251
14439
  }
14252
14440
  // senderOpenId 已在上方(force-topic grant 限制前)声明;这里只补 master 新增的 senderUnionId。
14253
14441
  const senderUnionId = data.sender?.sender_id?.union_id;
14442
+ const trustedCaller = trustedCallerForTurn(larkAppId, senderOpenId, senderUnionId);
14254
14443
  // union_id 信任腿(canOperate/evaluateTalk 的 teamBot)只对**飞书盖章的 bot 发送方**
14255
14444
  // 生效:平台 roster 是成员机器自报的,若不锁 sender_type,恶意成员把某个真人的
14256
14445
  // union_id 报成"自家 bot",那个真人就会在全团队机器上被当队友 bot 放行(talk +
@@ -14327,6 +14516,10 @@ async function handleNewTopicAdmitted(data, ctx) {
14327
14516
  let workflowGrillPrompt;
14328
14517
  const newTopicGrill = parseWorkflowGrillTrigger(cmdContent);
14329
14518
  if (newTopicGrill) {
14519
+ if (!isWorkflowFeatureEnabled()) {
14520
+ await sessionReply(anchor, WORKFLOW_DISABLED_MESSAGE, 'text', larkAppId);
14521
+ return;
14522
+ }
14330
14523
  if (await replyGrantRestrictionIfNeeded(larkAppId, chatId, senderOpenId, anchor, '/workflow')) {
14331
14524
  return;
14332
14525
  }
@@ -14550,9 +14743,20 @@ async function handleNewTopicAdmitted(data, ctx) {
14550
14743
  && buildQuoteHint(parsed, scope, anchor, localeForBot(larkAppId)) === ''
14551
14744
  && !ctx.forwardSeedData
14552
14745
  && !messageListener;
14746
+ // Session-group birth charges the ORIGINAL DM before creating the Feishu chat
14747
+ // (a quota denial must have zero external side effects), so by the time the
14748
+ // rewritten group turn gets here THIS message is already authorized AND
14749
+ // already paid for — see `alreadyAuthorizedAndCharged` in
14750
+ // enforceMessageQuotaForCliInput for why re-deciding it here can only lose
14751
+ // the unit we just took. `skipCharge` stays set as well so that even if that
14752
+ // short-circuit is ever removed, the worst case is a redundant recheck, never
14753
+ // a double charge.
14754
+ const sessionGroupAlreadyCharged = !!ctx.sessionGroupQuotaConsumed
14755
+ || quotaConsumedBeforeSessionGroupBirth;
14553
14756
  if (!await enforceMessageQuotaForCliInput(larkAppId, chatId, senderOpenId, messageId, anchor, teamTrustUnionId, senderUnionId, chatType, isBotSenderType, {
14554
14757
  listenerAuthorized: !!messageListener,
14555
- skipCharge: isBareForceTopic,
14758
+ skipCharge: isBareForceTopic || sessionGroupAlreadyCharged,
14759
+ alreadyAuthorizedAndCharged: sessionGroupAlreadyCharged,
14556
14760
  })) {
14557
14761
  return;
14558
14762
  }
@@ -14834,7 +15038,7 @@ async function handleNewTopicAdmitted(data, ctx) {
14834
15038
  // Ack reaction targets the ORIGINAL inbound message (2nd arg); the turn id
14835
15039
  // (5th arg) is the reply anchor so provenance holds on session-group births.
14836
15040
  await noteTurnReceived(ds, messageId, content, newTopicSender, replyAnchorId, substituteTrigger ? SUBSTITUTE_RECEIVED_REACTION_EMOJI_TYPE : undefined);
14837
- forkReservedInitialSession(ds, availableBots);
15041
+ forkReservedInitialSession(ds, availableBots, trustedCaller);
14838
15042
  // fork 成功即开场已交给 CLI;fork 抛错则开场只存在于内存,保持重发提示。
14839
15043
  markIngressAdmitted(ctx);
14840
15044
  const reason = oncallEntry
@@ -14886,7 +15090,7 @@ async function handleNewTopicAdmitted(data, ctx) {
14886
15090
  // Ack reaction targets the ORIGINAL inbound message (2nd arg); the turn id
14887
15091
  // (5th arg) is the reply anchor so provenance holds on session-group births.
14888
15092
  await noteTurnReceived(ds, messageId, content, newTopicSender, replyAnchorId, substituteTrigger ? SUBSTITUTE_RECEIVED_REACTION_EMOJI_TYPE : undefined);
14889
- forkReservedInitialSession(ds, availableBots);
15093
+ forkReservedInitialSession(ds, availableBots, trustedCaller);
14890
15094
  logger.info(`Session ${session.sessionId} ready (no projects to select), total active: ${getActiveCount()}`);
14891
15095
  }
14892
15096
  }
@@ -15092,6 +15296,15 @@ async function handleBotAdded(chatId, operatorOpenId, larkAppId, opts) {
15092
15296
  anchor = chatId;
15093
15297
  scope = 'chat';
15094
15298
  }
15299
+ // bot.added 是成员变更事件,没有用户 message_id 可作首轮 turn 身份。非 shared
15300
+ // 路径没有 shared reply seed 兜底,若以 pendingTurnId=undefined 直接 fork,
15301
+ // worker 发布的 managed_turn_origin 不含 turnId,CLI 首轮 `botmux send` 的
15302
+ // managed-origin attestation 会被 daemon 以 origin_unproven 拒绝——首轮回复
15303
+ // 发不回飞书。这里自行铸造首轮 turn 身份:
15304
+ // - thread scope:seed 消息本身就是可见锚点且是真实 om_ id,直接复用;
15305
+ // - chat scope:没有任何消息存在,铸造 daemon-owned 合成 turn id(同 trigger
15306
+ // 会话的 trg_ 约定),仅作身份标识,不冒充用户消息 id。
15307
+ const joinTurnId = scope === 'thread' ? anchor : `join_${randomUUID()}`;
15095
15308
  const dsKey = sessionKey(anchor, larkAppId);
15096
15309
  if (activeSessions.has(dsKey)) {
15097
15310
  logger.info(`[auto-start:入群] ${chatId.substring(0, 12)} 锚点已有会话,跳过`);
@@ -15134,7 +15347,9 @@ async function handleBotAdded(chatId, operatorOpenId, larkAppId, opts) {
15134
15347
  ownerOpenId: operatorOpenId,
15135
15348
  currentTurnTitle: title,
15136
15349
  workingDir: pinnedWorkingDir,
15137
- pendingTurnId: undefined,
15350
+ // 非 shared 路径的首轮 turn 身份(joinTurnId);shared 路径由
15351
+ // armSharedReplyTarget 用 shared seed id 覆盖。
15352
+ pendingTurnId: needsSharedReply ? undefined : joinTurnId,
15138
15353
  };
15139
15354
  const registration = await claimNewDaemonSession(activeSessions, ds);
15140
15355
  if (!registration.accepted) {
@@ -15147,7 +15362,9 @@ async function handleBotAdded(chatId, operatorOpenId, larkAppId, opts) {
15147
15362
  stageClaimedPendingRepoSetup(activeSessions, ds, {
15148
15363
  mode: autoWt ? 'auto_worktree' : 'picker',
15149
15364
  ...(autoWt && pinnedWorkingDir ? { baseDir: pinnedWorkingDir } : {}),
15150
- turnId: anchor,
15365
+ // 非 shared 路径复用首轮 turn 身份;shared 路径保留 anchor(随后被
15366
+ // armSharedReplyTarget 的 shared seed id 覆盖,此处仅作未 arm 时的兜底)。
15367
+ turnId: needsSharedReply ? anchor : joinTurnId,
15151
15368
  });
15152
15369
  }
15153
15370
  const joinBootstrapExternallyTakenOver = () => (activeSessions.get(dsKey) !== ds
@@ -15194,6 +15411,11 @@ async function handleBotAdded(chatId, operatorOpenId, larkAppId, opts) {
15194
15411
  const closing = closeSessionHelper(session.sessionId);
15195
15412
  if (activeSessions.get(dsKey) === ds)
15196
15413
  activeSessions.delete(dsKey);
15414
+ // 入群去重锚点同样只清自己登记的那条:shared 路径在登记锚点之前就可能
15415
+ // 回滚(此时 get 为 undefined,no-op);非 shared 路径在锚点登记之后
15416
+ // 回滚,不清掉会让后续 bot.added 带着 stale 锚点走进来。
15417
+ if (groupJoinAnchorByChat.get(chatLiveKey) === dsKey)
15418
+ groupJoinAnchorByChat.delete(chatLiveKey);
15197
15419
  await closing;
15198
15420
  };
15199
15421
  // Shared-mode seed/reply authority is externally visible. Publish it only
@@ -15244,6 +15466,28 @@ async function handleBotAdded(chatId, operatorOpenId, larkAppId, opts) {
15244
15466
  // Register the anchor so a later duplicate bot.added for this chat is deduped
15245
15467
  // even in 话题群 (where dsKey is the seed id, not chatId).
15246
15468
  groupJoinAnchorByChat.set(chatLiveKey, dsKey);
15469
+ // 持久化非 shared 首轮的 turn provenance(shared 路径已由 armSharedReplyTarget
15470
+ // 写入)。thread scope 以 seed 为 reply root;chat scope 无根,落 plain chat
15471
+ // 目标且不写 sender——首轮无真人发送者,--mention 回指天然走 incomplete →
15472
+ // 显式指定的安全方向。没有这条记录,worker 虽带 turnId fork,daemon 侧的
15473
+ // 首轮回复目标/参与者窗口仍无据可查。
15474
+ if (!needsSharedReply) {
15475
+ try {
15476
+ beginReplyTargetTurn(ds, scope === 'thread' ? anchor : undefined, joinTurnId, new Date(now).toISOString());
15477
+ sessionStore.updateSession(ds.session);
15478
+ }
15479
+ catch (err) {
15480
+ // 持久化失败(文件系统/数据库瞬态错误)时必须回滚已发布的会话:此时
15481
+ // 会话已进 activeSessions 且 groupJoinAnchorByChat 已登记,finally 只
15482
+ // settle barrier 和释放 in-flight 锁——留下 active 但 worker-null 的会话
15483
+ // 会让后续 bot.added 被无限去重,自动开工无法重试。同
15484
+ // stageClaimedPendingRepoSetup 的 unpublish/close 回滚。
15485
+ logger.warn(`[auto-start:入群] ${chatId.substring(0, 12)} 首轮 turn provenance 持久化失败,回滚会话注册:`
15486
+ + `${err instanceof Error ? err.message : String(err)}`);
15487
+ await rollbackRegisteredJoinSession();
15488
+ return;
15489
+ }
15490
+ }
15247
15491
  // Auto-worktree: register PENDING, build worktree off-path, commit+fork later.
15248
15492
  if (pinnedWorkingDir && autoWt) {
15249
15493
  if (await replyInvalidWorkingDirs(anchor, larkAppId, ds) || joinBootstrapWasTakenOver()) {
@@ -15347,17 +15591,10 @@ export const __testOnly_handleBotAdded = handleBotAdded;
15347
15591
  * than blocking the message.
15348
15592
  */
15349
15593
  function lookupForeignBotName(senderOpenId, larkAppId) {
15350
- try {
15351
- const fp = join(config.session.dataDir, `bot-openids-${larkAppId}.json`);
15352
- if (existsSync(fp)) {
15353
- const data = JSON.parse(readFileSync(fp, 'utf-8'));
15354
- for (const [name, openId] of Object.entries(data)) {
15355
- if (openId === senderOpenId)
15356
- return name;
15357
- }
15358
- }
15594
+ for (const [name, openId] of Object.entries(readPeerCrossRef(config.session.dataDir, larkAppId))) {
15595
+ if (openId === senderOpenId)
15596
+ return name;
15359
15597
  }
15360
- catch { /* fall through */ }
15361
15598
  try {
15362
15599
  const infoPath = join(config.session.dataDir, 'bots-info.json');
15363
15600
  if (existsSync(infoPath)) {
@@ -15488,6 +15725,7 @@ async function handleThreadReplyAdmitted(data, ctx, prepared) {
15488
15725
  // 类型的话,把真人 union_id 报成 bot 就能让真人在全团队被当队友 bot 放行
15489
15726
  //(talk + operate)。与旧联邦 team-bots「学习入口限 bot sender」同一不变量。
15490
15727
  const threadTeamTrustUnionId = (isBotSenderType || isForeignBot) ? threadSenderUnionId : undefined;
15728
+ const threadTrustedCaller = trustedCallerForTurn(larkAppId, threadSenderOpenId, threadSenderUnionId);
15491
15729
  const threadChatId = ctxChatId ?? data?.message?.chat_id;
15492
15730
  const clearAgentAttentionForHumanInbound = () => {
15493
15731
  if (isForeignBot || isBotSenderType)
@@ -15546,6 +15784,10 @@ async function handleThreadReplyAdmitted(data, ctx, prepared) {
15546
15784
  // `/template` 只保留退役提示。
15547
15785
  const threadGrill = parseWorkflowGrillTrigger(cmdContent);
15548
15786
  if (threadGrill) {
15787
+ if (!isWorkflowFeatureEnabled()) {
15788
+ await sessionReply(anchor, WORKFLOW_DISABLED_MESSAGE, 'text', larkAppId);
15789
+ return;
15790
+ }
15549
15791
  if (await replyGrantRestrictionIfNeeded(larkAppId, threadChatId, threadSenderOpenId, anchor, '/workflow')) {
15550
15792
  return;
15551
15793
  }
@@ -15785,35 +16027,38 @@ async function handleThreadReplyAdmitted(data, ctx, prepared) {
15785
16027
  // 回调 URL 已在上方 return,可用来中止)。答复权限 = canTalk,由
15786
16028
  // broker 在 submitCustomReply 内按注入的 canTalkChecker 判定:非授权人返回
15787
16029
  // 'unauthorized',这里 fall through 到正常路由。卡片由 broker.onSettle 自动 PATCH。
15788
- // `!threadGrill`:grill goal 分支只改写 promptContent 后 fall-through(不 return),
15789
- // cmdContent 仍是字面量 `/workflow new <目标>`;若不排除,待回答 ask 会把它当答案吞掉,
15790
- // grill 永远不启动。grill 必须穿过拦截器走正常转发。
15791
- if (threadSenderOpenId && threadChatId && !threadGrill) {
15792
- const askReplyText = cmdContent.trim();
15793
- if (askReplyText) {
15794
- const pendingAsk = findPendingAskByAnchor({ larkAppId, chatId: threadChatId, anchor });
15795
- if (pendingAsk) {
15796
- const outcome = submitCustomReply({
15797
- askId: pendingAsk.askId,
15798
- by: threadSenderOpenId,
15799
- text: askReplyText,
15800
- // Actor context so the broker's talk check uses the same predicate as
15801
- // the dispatcher gate / quota recheck: a bot text-reply → evaluateBotTalk
15802
- // (covers team-拉群 with no union_id), a platform teamMember human →
15803
- // evaluateTalk's teamMember union leg. Omitting it (card clicks) degrades
15804
- // to the plain evaluateTalk(openId, chatType).
15805
- actor: {
15806
- botSender: isBotSenderType || isForeignBot,
15807
- senderUnionId: threadTeamTrustUnionId,
15808
- memberUnionId: threadSenderUnionId,
15809
- },
15810
- });
15811
- if (outcome === 'accepted') {
15812
- logger.info(`[${anchor.substring(0, 12)}] ask custom reply accepted from ${threadSenderOpenId.substring(0, 12)}`);
15813
- return;
15814
- }
15815
- logger.info(`[${anchor.substring(0, 12)}] ask custom reply not accepted (${outcome}); falling through to normal routing`);
16030
+ // 「哪些消息算纯文字答复」收口在 askCustomReplyCandidate(含带资源消息与 grill
16031
+ // 触发的排除理由),拦截器这里只负责拿着结果去 settle。
16032
+ const askCandidate = askCustomReplyCandidate({
16033
+ senderOpenId: threadSenderOpenId,
16034
+ chatId: threadChatId,
16035
+ cmdContent,
16036
+ resourceCount: resources.length,
16037
+ isWorkflowGrillTrigger: !!threadGrill,
16038
+ });
16039
+ if (askCandidate) {
16040
+ const pendingAsk = findPendingAskByAnchor({ larkAppId, chatId: askCandidate.chatId, anchor });
16041
+ if (pendingAsk) {
16042
+ const outcome = submitCustomReply({
16043
+ askId: pendingAsk.askId,
16044
+ by: askCandidate.senderOpenId,
16045
+ text: askCandidate.text,
16046
+ // Actor context so the broker's talk check uses the same predicate as
16047
+ // the dispatcher gate / quota recheck: a bot text-reply → evaluateBotTalk
16048
+ // (covers team-拉群 with no union_id), a platform teamMember human →
16049
+ // evaluateTalk's teamMember union leg. Omitting it (card clicks) degrades
16050
+ // to the plain evaluateTalk(openId, chatType).
16051
+ actor: {
16052
+ botSender: isBotSenderType || isForeignBot,
16053
+ senderUnionId: threadTeamTrustUnionId,
16054
+ memberUnionId: threadSenderUnionId,
16055
+ },
16056
+ });
16057
+ if (outcome === 'accepted') {
16058
+ logger.info(`[${anchor.substring(0, 12)}] ask custom reply accepted from ${askCandidate.senderOpenId.substring(0, 12)}`);
16059
+ return;
15816
16060
  }
16061
+ logger.info(`[${anchor.substring(0, 12)}] ask custom reply not accepted (${outcome}); falling through to normal routing`);
15817
16062
  }
15818
16063
  }
15819
16064
  logger.info(`Reply in ${scope}-scope session ${anchor.substring(0, 12)}: ${content.substring(0, 100)} (resources: ${resources.length})`);
@@ -16402,7 +16647,7 @@ async function handleThreadReplyAdmitted(data, ctx, prepared) {
16402
16647
  ensureSessionWhiteboard(newDs);
16403
16648
  const availableBots = await getAvailableBots(larkAppId, autoCreateChatId);
16404
16649
  await noteTurnReceived(newDs, parsed.messageId, parsed.content, autoCreateSender, parsed.messageId, substituteTrigger ? SUBSTITUTE_RECEIVED_REACTION_EMOJI_TYPE : undefined);
16405
- forkReservedInitialSession(newDs, availableBots);
16650
+ forkReservedInitialSession(newDs, availableBots, threadTrustedCaller);
16406
16651
  // fork 成功即开场已交给 CLI;fork 抛错则开场只存在于内存,保持重发提示。
16407
16652
  markIngressAdmitted(ctx);
16408
16653
  const reason = oncallEntry
@@ -16442,7 +16687,7 @@ async function handleThreadReplyAdmitted(data, ctx, prepared) {
16442
16687
  ensureSessionWhiteboard(newDs);
16443
16688
  const availableBots = await getAvailableBots(larkAppId, autoCreateChatId);
16444
16689
  await noteTurnReceived(newDs, parsed.messageId, parsed.content, autoCreateSender, parsed.messageId, substituteTrigger ? SUBSTITUTE_RECEIVED_REACTION_EMOJI_TYPE : undefined);
16445
- forkReservedInitialSession(newDs, availableBots);
16690
+ forkReservedInitialSession(newDs, availableBots, threadTrustedCaller);
16446
16691
  }
16447
16692
  return;
16448
16693
  }
@@ -16520,7 +16765,10 @@ async function handleThreadReplyAdmitted(data, ctx, prepared) {
16520
16765
  // above (R4-B1); reuse the same frozen value here for the live-worker path.
16521
16766
  let accepted = false;
16522
16767
  try {
16523
- accepted = sendWorkerInput(ds, cliInput, parsed.messageId, codexAppSteerable ? { codexAppSteerable: true } : {});
16768
+ accepted = sendWorkerInput(ds, cliInput, parsed.messageId, {
16769
+ ...(codexAppSteerable ? { codexAppSteerable: true } : {}),
16770
+ ...(threadTrustedCaller ? { trustedCaller: threadTrustedCaller } : {}),
16771
+ });
16524
16772
  // Record the input as the session's last real CLI turn ONLY after the
16525
16773
  // worker accepted it. Recording before delivery (the old order) persisted
16526
16774
  // lastCliInput / lastUserPrompt / Codex-App sidecar for a turn that never
@@ -16616,6 +16864,8 @@ async function handleThreadReplyAdmitted(data, ctx, prepared) {
16616
16864
  // here, never re-inferred). System/recovery openings keep it absent.
16617
16865
  if (codexAppSteerable)
16618
16866
  currentFollowUp.codexAppSteerable = true;
16867
+ if (threadTrustedCaller)
16868
+ currentFollowUp.trustedCaller = threadTrustedCaller;
16619
16869
  admitQueuedActivationTail(ds, {
16620
16870
  userPrompt: promptContent,
16621
16871
  cliInput: currentFollowUp,
@@ -16642,6 +16892,9 @@ async function handleThreadReplyAdmitted(data, ctx, prepared) {
16642
16892
  resume: ds.session.queuedActivationResume ?? ds.hasHistory,
16643
16893
  turnId: ds.session.queuedActivationTurnId,
16644
16894
  dispatchAttempt: ds.session.queuedActivationDispatchAttempt,
16895
+ ...(ds.session.queuedActivationInput?.trustedCaller
16896
+ ? { trustedCaller: ds.session.queuedActivationInput.trustedCaller }
16897
+ : {}),
16645
16898
  });
16646
16899
  if (ownsInitialStartClaim())
16647
16900
  retainInitialStartClaim = true;
@@ -16667,6 +16920,7 @@ async function handleThreadReplyAdmitted(data, ctx, prepared) {
16667
16920
  resume: ds.hasHistory,
16668
16921
  turnId: ds.session.queuedActivationTurnId,
16669
16922
  dispatchAttempt: ds.session.queuedActivationDispatchAttempt,
16923
+ ...(retainedQueuedActivation.trustedCaller ? { trustedCaller: retainedQueuedActivation.trustedCaller } : {}),
16670
16924
  });
16671
16925
  if (ownsInitialStartClaim())
16672
16926
  retainInitialStartClaim = true;
@@ -16789,6 +17043,9 @@ async function handleThreadReplyAdmitted(data, ctx, prepared) {
16789
17043
  if (codexAppSteerable && !queuedDashboardTurn && !queuedHasDurableTail) {
16790
17044
  wrappedInput.codexAppSteerable = true;
16791
17045
  }
17046
+ if (threadTrustedCaller && !queuedHasDurableTail) {
17047
+ wrappedInput.trustedCaller = threadTrustedCaller;
17048
+ }
16792
17049
  reforkAccepted = forkWorker(ds, wrappedInput, {
16793
17050
  // See `hadPriorCliInput` above — an opening on a CLI that never took any
16794
17051
  // input cold-spawns rather than `--resume`-ing an empty session.
@@ -16796,6 +17053,7 @@ async function handleThreadReplyAdmitted(data, ctx, prepared) {
16796
17053
  turnId: queuedHasDurableTail
16797
17054
  ? (ds.session.queuedActivationTurnId ?? `queued-opening:${ds.session.sessionId}`)
16798
17055
  : parsed.messageId,
17056
+ ...(wrappedInput.trustedCaller ? { trustedCaller: wrappedInput.trustedCaller } : {}),
16799
17057
  });
16800
17058
  }
16801
17059
  }
@@ -17476,6 +17734,18 @@ async function waitForManagedActivationCommit(index, appId) {
17476
17734
  }
17477
17735
  }
17478
17736
  export async function startDaemon(botIndex) {
17737
+ // Survive a fire-and-forget rejection instead of dying from it. Installed here
17738
+ // rather than in index-daemon.ts on purpose: startDaemon has TWO entry points
17739
+ // (index-daemon.ts and index-core-only.ts), so guarding the entry file would
17740
+ // leave the other one unprotected. Idempotent, so an entry point may also call
17741
+ // it earlier without harm.
17742
+ //
17743
+ // Node's default is to terminate the process, which is right for a worker (it
17744
+ // owns one session) and wrong for the daemon (it owns every session): one
17745
+ // missed `.catch` on one session's teardown would otherwise end every other
17746
+ // live Lark session too. The rejection is logged loudly at error level — this
17747
+ // is a backstop for the next missed `.catch`, not permission to omit them.
17748
+ installDaemonRejectionGuard(logger);
17479
17749
  // Repair a shared tmux server polluted by an older botmux immediately on
17480
17750
  // daemon startup. This must not depend on restoring/spawning a bmx-* session:
17481
17751
  // a user-held tmux server can outlive every botmux pane and still leak stale
@@ -17892,8 +18162,7 @@ export async function startDaemon(botIndex) {
17892
18162
  // status becomes `closed` → trigger-result resolves `failed` (soft terminal),
17893
18163
  // and resolveIdempotencyHit sees a not-live session on retry. This is the
17894
18164
  // observable fail-closed path the round-8 finding requires.
17895
- void closeSessionHelper(ds.session.sessionId).catch(() => { });
17896
- logger.error(`[idempotency] fail-closed session ${ds.session.sessionId.slice(0, 8)} after exit-convergence write failure`);
18165
+ void runIdempotencyFailClose(ds.session.sessionId, closeSessionForBackgroundCleanup, logger);
17897
18166
  }
17898
18167
  };
17899
18168
  // Initialise worker pool with daemon callbacks
@@ -17914,9 +18183,7 @@ export async function startDaemon(botIndex) {
17914
18183
  logger.info(`[${sessionId.substring(0, 8)}] Withdraw auto-close deferred: Codex App FIFO became unsettled`);
17915
18184
  return false;
17916
18185
  }
17917
- await closeSessionHelper(sessionId);
17918
- logger.info(`[${sessionId.substring(0, 8)}] Session auto-closed (message withdrawn)`);
17919
- return true;
18186
+ return runWithdrawAutoClose(sessionId, closeSessionForBackgroundCleanup, logger);
17920
18187
  }).catch((err) => {
17921
18188
  logger.warn(`[${sessionId.substring(0, 8)}] Withdraw auto-close mutation failed; session retained: `
17922
18189
  + `${err instanceof Error ? err.message : String(err)}`);
@@ -18039,6 +18306,20 @@ export async function startDaemon(botIndex) {
18039
18306
  logger.error(`[idempotency] boot reconcile failed to converge — aborting bot startup (fail-closed): ${err instanceof Error ? err.message : err}`);
18040
18307
  throw err instanceof Error ? err : new Error(String(err));
18041
18308
  }
18309
+ // Release durable mojo containment handles a REBOOT provably killed, BEFORE the
18310
+ // device-isolation activation inventory ever synthesises a blocker from them —
18311
+ // otherwise a post-reboot handle keeps blocking activation forever (round-11
18312
+ // P1-1). Daemon-global and idempotent: guarded so it runs once no matter how
18313
+ // many bots start; any store/proc read failure fails closed inside the helper.
18314
+ if (!mojoContainmentReconciledThisBoot) {
18315
+ mojoContainmentReconciledThisBoot = true;
18316
+ try {
18317
+ reconcileContainmentHandlesOnBoot();
18318
+ }
18319
+ catch (err) {
18320
+ logger.warn(`[mojo] containment boot reconciliation threw (blockers retained): ${err instanceof Error ? err.message : String(err)}`);
18321
+ }
18322
+ }
18042
18323
  // Seed dashboard IPC botName with the custom displayName (falling back to the
18043
18324
  // bot's config id); the friendly name from /bot/v3/info is wired into the
18044
18325
  // registry descriptor (below) but the IPC server also needs its own copy for
@@ -18401,6 +18682,12 @@ export async function startDaemon(botIndex) {
18401
18682
  // Restore complete → /api/asks may now safely 403 unknown sessions again; a
18402
18683
  // reconnecting ask hook that raced the restore got retryable 503s until here.
18403
18684
  sessionsRestored = true;
18685
+ // Freeze the control plane of restored mojo sessions NOW, not at their next
18686
+ // worker fork. Restore completes before the dispatcher can deliver a message,
18687
+ // so this closes the window where an operator edits the bot's endpoint/workspace
18688
+ // between the restart and a session's next wake — which would otherwise let that
18689
+ // session adopt the NEW control plane as its original identity.
18690
+ migrateMojoSessionIdentities(activeSessions);
18404
18691
  // Now that activeSessions is populated, release the forward-followup flush
18405
18692
  // barrier. Persisted seeds were loaded into the buffer at dispatcher startup
18406
18693
  // (so root-linked clarifications can pair with them), but their flushes wait
@@ -18529,6 +18816,45 @@ export async function startDaemon(botIndex) {
18529
18816
  enforceLiveSessionCap('periodic');
18530
18817
  }, 60_000);
18531
18818
  idleWorkerSweepTimer.unref?.();
18819
+ const sessionOwnerReminder = new SessionOwnerReminderController({
18820
+ load: () => loadSessionOwnerReminderRecords(config.session.dataDir, cfg.larkAppId),
18821
+ save: records => saveSessionOwnerReminderRecords(config.session.dataDir, cfg.larkAppId, records),
18822
+ canSend: ds => larkTransportEnabled({
18823
+ chatId: ds.chatId,
18824
+ apiOnly: getBot(ds.larkAppId).config.apiOnly,
18825
+ }),
18826
+ send: async (ds, text, uuid) => {
18827
+ await sendSessionOwnerThreadNotification({
18828
+ larkAppId: ds.larkAppId,
18829
+ rootMessageId: ds.session.rootMessageId,
18830
+ ownerOpenId: ds.session.ownerOpenId,
18831
+ }, text, uuid);
18832
+ },
18833
+ onError: (ds, error) => logger.warn(`[session-owner-reminder] send failed session=${ds.session.sessionId}: `
18834
+ + `${error instanceof Error ? error.message : String(error)}`),
18835
+ });
18836
+ let sessionOwnerReminderScanInFlight = false;
18837
+ const scanSessionOwnerReminders = async () => {
18838
+ if (sessionOwnerReminderScanInFlight)
18839
+ return;
18840
+ sessionOwnerReminderScanInFlight = true;
18841
+ try {
18842
+ const currentConfig = getBot(cfg.larkAppId).config.sessionOwnerReminder
18843
+ ?? DEFAULT_SESSION_OWNER_REMINDER;
18844
+ await sessionOwnerReminder.scan(activeSessions.values(), currentConfig);
18845
+ }
18846
+ catch (error) {
18847
+ logger.warn(`[session-owner-reminder] scan failed: ${error instanceof Error ? error.message : String(error)}`);
18848
+ }
18849
+ finally {
18850
+ sessionOwnerReminderScanInFlight = false;
18851
+ }
18852
+ };
18853
+ const sessionOwnerReminderTimer = setInterval(() => {
18854
+ void scanSessionOwnerReminders();
18855
+ }, 60_000);
18856
+ sessionOwnerReminderTimer.unref?.();
18857
+ void scanSessionOwnerReminders();
18532
18858
  // Periodic sandbox reconciler: the daemon's SIGKILL straggler-reaper (and any
18533
18859
  // worker SIGKILL) bypasses worker-side killCli(), so the pre-created deny-mask
18534
18860
  // mountpoints + per-session tree of a killed-but-still-active sandboxed session
@@ -18707,8 +19033,8 @@ export async function startDaemon(botIndex) {
18707
19033
  }, 30_000);
18708
19034
  overloadTimer.unref?.();
18709
19035
  }
18710
- // Graceful shutdown. Riff owners first run a three-phase non-cancelling drain
18711
- // that durably ACKs every exact final task lineage as one fleet transaction.
19036
+ // Graceful shutdown. Remote owners first run a three-phase non-cancelling
19037
+ // drain that durably ACKs every exact final lineage as one fleet transaction.
18712
19038
  // No worker exits until every participant is prepared and fresh-verified.
18713
19039
  // Ordinary workers then receive SIGTERM / close IPC and the daemon waits up
18714
19040
  // to DAEMON_WORKER_EXIT_GRACE_MS for them to exit
@@ -18735,23 +19061,23 @@ export async function startDaemon(botIndex) {
18735
19061
  logger.error(`Daemon remains online: ${initialShutdownFleet.error}`);
18736
19062
  return;
18737
19063
  }
18738
- // Preflight Riff before stopping services/removing descriptors. A failed
19064
+ // Preflight all remote backends before stopping services/removing descriptors. A failed
18739
19065
  // drain or persistence write aborts every successfully prepared peer, so a
18740
19066
  // refused shutdown returns to a live fleet instead of leaving an early
18741
19067
  // participant workerless.
18742
- const riffCandidates = initialShutdownFleet.sessions
18743
- .filter(ds => shutdownBackendDisposition(ds) === 'riff-drain-detach');
18744
- const riffPrepareResults = await prepareRiffFleetForShutdown(riffCandidates, {
19068
+ const remoteCandidates = initialShutdownFleet.sessions
19069
+ .filter(ds => shutdownBackendDisposition(ds) === 'remote-drain-detach');
19070
+ const remotePrepareResults = await prepareRemoteFleetForShutdown(remoteCandidates, {
18745
19071
  deadlineMs: shutdownDeadlineMs,
18746
19072
  });
18747
- const riffPrepared = riffPrepareResults.filter((entry) => entry.result.ok);
18748
- const riffFenced = riffPrepareResults.filter((entry) => entry.result.fence !== 'none');
18749
- const abortRiffFleet = async (reason, retainFenced = new Set()) => {
19073
+ const remotePrepared = remotePrepareResults.filter((entry) => entry.result.ok);
19074
+ const remoteFenced = remotePrepareResults.filter((entry) => entry.result.fence !== 'none');
19075
+ const abortRemoteFleet = async (reason, retainFenced = new Set()) => {
18750
19076
  for (const ds of retainFenced) {
18751
- logger.error(`[${ds.session.sessionId.slice(0, 8)}] Riff shutdown participant remains fenced: `
19077
+ logger.error(`[${ds.session.sessionId.slice(0, 8)}] Remote shutdown participant remains fenced: `
18752
19078
  + 'durable/runtime ownership could not be reconciled safely');
18753
19079
  }
18754
- const aborts = await abortRiffShutdownFleet(riffFenced
19080
+ const aborts = await abortRemoteShutdownFleet(remoteFenced
18755
19081
  .filter(({ ds }) => !retainFenced.has(ds))
18756
19082
  .map(({ ds, result }) => ({ ds, result })), {
18757
19083
  deadlineMs: shutdownDeadlineMs,
@@ -18759,34 +19085,34 @@ export async function startDaemon(botIndex) {
18759
19085
  for (const { ds, result } of aborts) {
18760
19086
  if (result.ok)
18761
19087
  continue;
18762
- logger.error(`[${ds.session.sessionId.slice(0, 8)}] Riff shutdown rollback was not ACKed: `
19088
+ logger.error(`[${ds.session.sessionId.slice(0, 8)}] Remote shutdown rollback was not ACKed: `
18763
19089
  + `${result.error ?? 'unknown'}; admission remains fail-closed`);
18764
19090
  }
18765
19091
  setSessionLifecycleShutdown(false);
18766
19092
  shuttingDown = false;
18767
19093
  logger.error(`Daemon remains online: ${reason}`);
18768
19094
  };
18769
- const riffPrepareFailures = riffPrepareResults.filter(entry => !entry.result.ok);
18770
- if (riffPrepareFailures.length > 0) {
18771
- for (const { ds, result } of riffPrepareFailures) {
19095
+ const remotePrepareFailures = remotePrepareResults.filter(entry => !entry.result.ok);
19096
+ if (remotePrepareFailures.length > 0) {
19097
+ for (const { ds, result } of remotePrepareFailures) {
18772
19098
  if (result.ok)
18773
19099
  continue;
18774
19100
  logger.error(`[${ds.session.sessionId.slice(0, 8)}] Daemon shutdown prepare refused: `
18775
19101
  + `${result.error}${result.taskId ? ` (task ${result.taskId})` : ''}`);
18776
19102
  }
18777
- await abortRiffFleet(`${riffPrepareFailures.length} Riff owner(s) could not be safely drained`);
19103
+ await abortRemoteFleet(`${remotePrepareFailures.length} remote owner(s) could not be safely drained`);
18778
19104
  return;
18779
19105
  }
18780
- const riffPersistence = persistPreparedRiffShutdownFleet(riffPrepared, {
19106
+ const remotePersistence = persistPreparedRemoteShutdownFleet(remotePrepared, {
18781
19107
  deadlineMs: shutdownDeadlineMs,
18782
19108
  });
18783
- if (!riffPersistence.ok) {
18784
- const retainIds = new Set(riffPersistence.retainFencedSessionIds);
18785
- const retainFenced = new Set(riffPrepared
19109
+ if (!remotePersistence.ok) {
19110
+ const retainIds = new Set(remotePersistence.retainFencedSessionIds);
19111
+ const retainFenced = new Set(remotePrepared
18786
19112
  .filter(({ ds }) => retainIds.has(ds.session.sessionId))
18787
19113
  .map(({ ds }) => ds));
18788
- logger.error(`Daemon shutdown lineage verification refused: ${riffPersistence.error}`);
18789
- await abortRiffFleet(`${riffPersistence.sessionIds.length} Riff lineage row(s) could not be fresh-verified`, retainFenced);
19114
+ logger.error(`Daemon shutdown lineage verification refused: ${remotePersistence.error}`);
19115
+ await abortRemoteFleet(`${remotePersistence.sessionIds.length} remote lineage row(s) could not be fresh-verified`, retainFenced);
18790
19116
  return;
18791
19117
  }
18792
19118
  // Recheck generation ownership while every participant is still fenced and
@@ -18794,33 +19120,33 @@ export async function startDaemon(botIndex) {
18794
19120
  const currentShutdownFleet = collectUniqueDaemonShutdownSessions(activeSessions.values());
18795
19121
  if (!currentShutdownFleet.ok) {
18796
19122
  const ambiguousSessionId = currentShutdownFleet.sessionId;
18797
- await abortRiffFleet(`Riff ownership became ambiguous after shutdown preflight: ${currentShutdownFleet.error}`, new Set(riffPrepared
19123
+ await abortRemoteFleet(`Remote ownership became ambiguous after shutdown preflight: ${currentShutdownFleet.error}`, new Set(remotePrepared
18798
19124
  .filter(({ ds }) => ds.session.sessionId === ambiguousSessionId)
18799
19125
  .map(({ ds }) => ds)));
18800
19126
  return;
18801
19127
  }
18802
- const currentRiffOwners = currentShutdownFleet.sessions
18803
- .filter(ds => shutdownBackendDisposition(ds) === 'riff-drain-detach');
18804
- const riffPreparedOwners = new Set(riffPrepared.map(entry => entry.ds));
18805
- const riffGenerationMismatch = currentRiffOwners.some(ds => !riffPreparedOwners.has(ds))
18806
- || riffPrepared.some(({ ds, result }) => !currentRiffOwners.includes(ds) || !isPreparedRiffSessionCurrent(ds, result));
18807
- if (riffGenerationMismatch) {
18808
- const retainFenced = new Set(riffPrepared
18809
- .filter(({ ds, result }) => (!currentRiffOwners.includes(ds) || !isPreparedRiffSessionCurrent(ds, result))
18810
- && !canAbortVerifiedExitedRiffPreparation(ds, result))
19128
+ const currentRemoteOwners = currentShutdownFleet.sessions
19129
+ .filter(ds => shutdownBackendDisposition(ds) === 'remote-drain-detach');
19130
+ const remotePreparedOwners = new Set(remotePrepared.map(entry => entry.ds));
19131
+ const remoteGenerationMismatch = currentRemoteOwners.some(ds => !remotePreparedOwners.has(ds))
19132
+ || remotePrepared.some(({ ds, result }) => !currentRemoteOwners.includes(ds) || !isPreparedRemoteSessionCurrent(ds, result));
19133
+ if (remoteGenerationMismatch) {
19134
+ const retainFenced = new Set(remotePrepared
19135
+ .filter(({ ds, result }) => (!currentRemoteOwners.includes(ds) || !isPreparedRemoteSessionCurrent(ds, result))
19136
+ && !canAbortVerifiedExitedRemotePreparation(ds, result))
18811
19137
  .map(({ ds }) => ds));
18812
- await abortRiffFleet('Riff ownership changed after shutdown preflight', retainFenced);
19138
+ await abortRemoteFleet('Remote ownership changed after shutdown preflight', retainFenced);
18813
19139
  return;
18814
19140
  }
18815
19141
  // Validate-all above, then commit-all synchronously with no await or
18816
19142
  // callback boundary between peers.
18817
- const riffRetiredWorkers = [];
18818
- for (const { ds, result } of riffPrepared) {
18819
- if (!commitPreparedRiffShutdown(ds, result)) {
18820
- throw new Error(`Riff shutdown commit invariant lost for ${ds.session.sessionId}`);
19143
+ const remoteRetiredWorkers = [];
19144
+ for (const { ds, result } of remotePrepared) {
19145
+ if (!commitPreparedRemoteShutdown(ds, result)) {
19146
+ throw new Error(`Remote shutdown commit invariant lost for ${ds.session.sessionId}`);
18821
19147
  }
18822
19148
  if (result.worker)
18823
- riffRetiredWorkers.push(result.worker);
19149
+ remoteRetiredWorkers.push(result.worker);
18824
19150
  }
18825
19151
  scheduler.stopScheduler();
18826
19152
  // NOTE: turn-terminal queue drain + webhook dispatcher stop are deliberately
@@ -18860,6 +19186,7 @@ export async function startDaemon(botIndex) {
18860
19186
  deleteVcMeetingPendingInvite(key);
18861
19187
  clearInterval(descriptorHeartbeat);
18862
19188
  clearInterval(idleWorkerSweepTimer);
19189
+ clearInterval(sessionOwnerReminderTimer);
18863
19190
  if (memoryDiagnostics)
18864
19191
  clearInterval(memoryDiagnostics);
18865
19192
  removeDaemonDescriptor(cfg.larkAppId);
@@ -18905,7 +19232,10 @@ export async function startDaemon(botIndex) {
18905
19232
  }
18906
19233
  }
18907
19234
  };
18908
- for (const worker of riffRetiredWorkers)
19235
+ // Union of two renames: this branch generalised the collection
19236
+ // riffRetiredWorkers -> remoteRetiredWorkers (it now carries mojo too), while
19237
+ // master renamed the local helper trackWorkerExit -> trackWorker.
19238
+ for (const worker of remoteRetiredWorkers)
18909
19239
  trackWorker(worker);
18910
19240
  for (const ds of currentShutdownFleet.sessions) {
18911
19241
  const w = ds.worker;
@@ -18922,8 +19252,8 @@ export async function startDaemon(botIndex) {
18922
19252
  if (!w.killed) {
18923
19253
  logger.info(`Shutting down worker for session ${ds.session.sessionId}`);
18924
19254
  const disposition = shutdownBackendDisposition(ds);
18925
- if (disposition === 'riff-drain-detach') {
18926
- throw new Error(`undrained Riff generation after atomic commit: ${ds.session.sessionId}`);
19255
+ if (disposition === 'remote-drain-detach') {
19256
+ throw new Error(`undrained remote generation after atomic commit: ${ds.session.sessionId}`);
18927
19257
  }
18928
19258
  // Branch by the session's FROZEN backend (stamped on Session.backendType
18929
19259
  // at spawn), NOT the bot's live config — a dashboard backendType edit must
@@ -19072,6 +19402,7 @@ export async function startDaemon(botIndex) {
19072
19402
  setSupervisorShutdownHandler(null);
19073
19403
  clearInterval(descriptorHeartbeat);
19074
19404
  clearInterval(idleWorkerSweepTimer);
19405
+ clearInterval(sessionOwnerReminderTimer);
19075
19406
  clearInterval(docCommentPollTimer);
19076
19407
  if (memoryDiagnostics)
19077
19408
  clearInterval(memoryDiagnostics);