botmux 3.5.0 → 3.6.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 (505) hide show
  1. package/README.en.md +56 -396
  2. package/README.md +55 -396
  3. package/dist/.runtime-build-id +1 -0
  4. package/dist/adapters/backend/sandbox.d.ts +99 -279
  5. package/dist/adapters/backend/sandbox.d.ts.map +1 -1
  6. package/dist/adapters/backend/sandbox.js +424 -826
  7. package/dist/adapters/backend/sandbox.js.map +1 -1
  8. package/dist/adapters/cli/claude-code.d.ts +6 -0
  9. package/dist/adapters/cli/claude-code.d.ts.map +1 -1
  10. package/dist/adapters/cli/claude-code.js +197 -11
  11. package/dist/adapters/cli/claude-code.js.map +1 -1
  12. package/dist/adapters/cli/coco.js +4 -4
  13. package/dist/adapters/cli/coco.js.map +1 -1
  14. package/dist/adapters/cli/codex-app.d.ts.map +1 -1
  15. package/dist/adapters/cli/codex-app.js +9 -6
  16. package/dist/adapters/cli/codex-app.js.map +1 -1
  17. package/dist/adapters/cli/codex.d.ts.map +1 -1
  18. package/dist/adapters/cli/codex.js +9 -5
  19. package/dist/adapters/cli/codex.js.map +1 -1
  20. package/dist/adapters/cli/fs-policy.d.ts +244 -0
  21. package/dist/adapters/cli/fs-policy.d.ts.map +1 -0
  22. package/dist/adapters/cli/fs-policy.js +537 -0
  23. package/dist/adapters/cli/fs-policy.js.map +1 -0
  24. package/dist/adapters/cli/grok.d.ts +2 -1
  25. package/dist/adapters/cli/grok.d.ts.map +1 -1
  26. package/dist/adapters/cli/grok.js +2 -1
  27. package/dist/adapters/cli/grok.js.map +1 -1
  28. package/dist/adapters/cli/hermes.d.ts.map +1 -1
  29. package/dist/adapters/cli/hermes.js +8 -4
  30. package/dist/adapters/cli/hermes.js.map +1 -1
  31. package/dist/adapters/cli/mtr.d.ts.map +1 -1
  32. package/dist/adapters/cli/mtr.js +3 -2
  33. package/dist/adapters/cli/mtr.js.map +1 -1
  34. package/dist/adapters/cli/opencode.d.ts.map +1 -1
  35. package/dist/adapters/cli/opencode.js +5 -4
  36. package/dist/adapters/cli/opencode.js.map +1 -1
  37. package/dist/adapters/cli/read-isolation.d.ts +14 -200
  38. package/dist/adapters/cli/read-isolation.d.ts.map +1 -1
  39. package/dist/adapters/cli/read-isolation.js +18 -477
  40. package/dist/adapters/cli/read-isolation.js.map +1 -1
  41. package/dist/adapters/cli/relay.js +2 -2
  42. package/dist/adapters/cli/relay.js.map +1 -1
  43. package/dist/adapters/cli/runner-input.d.ts +2 -2
  44. package/dist/adapters/cli/runner-input.d.ts.map +1 -1
  45. package/dist/adapters/cli/runner-input.js +9 -6
  46. package/dist/adapters/cli/runner-input.js.map +1 -1
  47. package/dist/adapters/cli/seed.d.ts.map +1 -1
  48. package/dist/adapters/cli/seed.js +2 -1
  49. package/dist/adapters/cli/seed.js.map +1 -1
  50. package/dist/adapters/cli/traex.d.ts.map +1 -1
  51. package/dist/adapters/cli/traex.js +3 -2
  52. package/dist/adapters/cli/traex.js.map +1 -1
  53. package/dist/adapters/cli/types.d.ts +25 -18
  54. package/dist/adapters/cli/types.d.ts.map +1 -1
  55. package/dist/bot-registry.d.ts +54 -23
  56. package/dist/bot-registry.d.ts.map +1 -1
  57. package/dist/bot-registry.js +91 -0
  58. package/dist/bot-registry.js.map +1 -1
  59. package/dist/cli.d.ts.map +1 -1
  60. package/dist/cli.js +297 -104
  61. package/dist/cli.js.map +1 -1
  62. package/dist/codex-app-runner.js +85 -192
  63. package/dist/codex-app-runner.js.map +1 -1
  64. package/dist/config.d.ts +0 -16
  65. package/dist/config.d.ts.map +1 -1
  66. package/dist/config.js +0 -30
  67. package/dist/config.js.map +1 -1
  68. package/dist/core/ask-broker.d.ts +12 -0
  69. package/dist/core/ask-broker.d.ts.map +1 -1
  70. package/dist/core/ask-broker.js +41 -0
  71. package/dist/core/ask-broker.js.map +1 -1
  72. package/dist/core/bot-turn-mutation-gate.d.ts +24 -0
  73. package/dist/core/bot-turn-mutation-gate.d.ts.map +1 -0
  74. package/dist/core/bot-turn-mutation-gate.js +369 -0
  75. package/dist/core/bot-turn-mutation-gate.js.map +1 -0
  76. package/dist/core/command-handler.d.ts +5 -0
  77. package/dist/core/command-handler.d.ts.map +1 -1
  78. package/dist/core/command-handler.js +53 -73
  79. package/dist/core/command-handler.js.map +1 -1
  80. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  81. package/dist/core/dashboard-ipc-server.js +356 -84
  82. package/dist/core/dashboard-ipc-server.js.map +1 -1
  83. package/dist/core/dashboard-rows.d.ts +8 -0
  84. package/dist/core/dashboard-rows.d.ts.map +1 -1
  85. package/dist/core/dashboard-rows.js.map +1 -1
  86. package/dist/core/host-overload-alert.d.ts +157 -0
  87. package/dist/core/host-overload-alert.d.ts.map +1 -0
  88. package/dist/core/host-overload-alert.js +251 -0
  89. package/dist/core/host-overload-alert.js.map +1 -0
  90. package/dist/core/passthrough-commands.d.ts +8 -0
  91. package/dist/core/passthrough-commands.d.ts.map +1 -1
  92. package/dist/core/passthrough-commands.js +20 -1
  93. package/dist/core/passthrough-commands.js.map +1 -1
  94. package/dist/core/plugins/mcp/gateway-installer.d.ts.map +1 -1
  95. package/dist/core/plugins/mcp/gateway-installer.js +14 -2
  96. package/dist/core/plugins/mcp/gateway-installer.js.map +1 -1
  97. package/dist/core/restart-coordinator.d.ts +28 -0
  98. package/dist/core/restart-coordinator.d.ts.map +1 -0
  99. package/dist/core/restart-coordinator.js +82 -0
  100. package/dist/core/restart-coordinator.js.map +1 -0
  101. package/dist/core/restart-followup-policy.d.ts +112 -0
  102. package/dist/core/restart-followup-policy.d.ts.map +1 -0
  103. package/dist/core/restart-followup-policy.js +49 -0
  104. package/dist/core/restart-followup-policy.js.map +1 -0
  105. package/dist/core/role-library.d.ts +1 -1
  106. package/dist/core/role-library.js +1 -1
  107. package/dist/core/session-cwd.d.ts.map +1 -1
  108. package/dist/core/session-cwd.js +8 -0
  109. package/dist/core/session-cwd.js.map +1 -1
  110. package/dist/core/session-discovery.d.ts +14 -0
  111. package/dist/core/session-discovery.d.ts.map +1 -1
  112. package/dist/core/session-discovery.js +25 -0
  113. package/dist/core/session-discovery.js.map +1 -1
  114. package/dist/core/session-liveness.d.ts +15 -0
  115. package/dist/core/session-liveness.d.ts.map +1 -0
  116. package/dist/core/session-liveness.js +76 -0
  117. package/dist/core/session-liveness.js.map +1 -0
  118. package/dist/core/session-manager.d.ts.map +1 -1
  119. package/dist/core/session-manager.js +90 -60
  120. package/dist/core/session-manager.js.map +1 -1
  121. package/dist/core/session-row-enrichment.d.ts +15 -0
  122. package/dist/core/session-row-enrichment.d.ts.map +1 -0
  123. package/dist/core/session-row-enrichment.js +93 -0
  124. package/dist/core/session-row-enrichment.js.map +1 -0
  125. package/dist/core/slash-inject.js +1 -1
  126. package/dist/core/slash-inject.js.map +1 -1
  127. package/dist/core/trigger-final-suppression.d.ts +24 -0
  128. package/dist/core/trigger-final-suppression.d.ts.map +1 -0
  129. package/dist/core/trigger-final-suppression.js +97 -0
  130. package/dist/core/trigger-final-suppression.js.map +1 -0
  131. package/dist/core/trigger-session.d.ts.map +1 -1
  132. package/dist/core/trigger-session.js +64 -4
  133. package/dist/core/trigger-session.js.map +1 -1
  134. package/dist/core/types.d.ts +6 -0
  135. package/dist/core/types.d.ts.map +1 -1
  136. package/dist/core/types.js.map +1 -1
  137. package/dist/core/worker-pool.d.ts +44 -0
  138. package/dist/core/worker-pool.d.ts.map +1 -1
  139. package/dist/core/worker-pool.js +333 -38
  140. package/dist/core/worker-pool.js.map +1 -1
  141. package/dist/daemon.d.ts +5 -1
  142. package/dist/daemon.d.ts.map +1 -1
  143. package/dist/daemon.js +526 -35
  144. package/dist/daemon.js.map +1 -1
  145. package/dist/dashboard/aggregator.d.ts +1 -0
  146. package/dist/dashboard/aggregator.d.ts.map +1 -1
  147. package/dist/dashboard/aggregator.js +34 -6
  148. package/dist/dashboard/aggregator.js.map +1 -1
  149. package/dist/dashboard/bot-payload.d.ts +8 -0
  150. package/dist/dashboard/bot-payload.d.ts.map +1 -1
  151. package/dist/dashboard/bot-payload.js +10 -0
  152. package/dist/dashboard/bot-payload.js.map +1 -1
  153. package/dist/dashboard/compat.d.ts +102 -2
  154. package/dist/dashboard/compat.d.ts.map +1 -1
  155. package/dist/dashboard/compat.js +78 -14
  156. package/dist/dashboard/compat.js.map +1 -1
  157. package/dist/dashboard/connector-api.d.ts.map +1 -1
  158. package/dist/dashboard/connector-api.js +1 -0
  159. package/dist/dashboard/connector-api.js.map +1 -1
  160. package/dist/dashboard/desktop-asks.d.ts +21 -0
  161. package/dist/dashboard/desktop-asks.d.ts.map +1 -0
  162. package/dist/dashboard/desktop-asks.js +49 -0
  163. package/dist/dashboard/desktop-asks.js.map +1 -0
  164. package/dist/dashboard/public-redact.d.ts +7 -0
  165. package/dist/dashboard/public-redact.d.ts.map +1 -1
  166. package/dist/dashboard/public-redact.js +35 -3
  167. package/dist/dashboard/public-redact.js.map +1 -1
  168. package/dist/dashboard/session-presentation.d.ts +16 -0
  169. package/dist/dashboard/session-presentation.d.ts.map +1 -0
  170. package/dist/dashboard/session-presentation.js +62 -0
  171. package/dist/dashboard/session-presentation.js.map +1 -0
  172. package/dist/dashboard/settings-write-applier.d.ts +2 -1
  173. package/dist/dashboard/settings-write-applier.d.ts.map +1 -1
  174. package/dist/dashboard/settings-write-applier.js +19 -1
  175. package/dist/dashboard/settings-write-applier.js.map +1 -1
  176. package/dist/dashboard/trigger-api.d.ts.map +1 -1
  177. package/dist/dashboard/trigger-api.js +19 -1
  178. package/dist/dashboard/trigger-api.js.map +1 -1
  179. package/dist/dashboard/vc-consumer-profiles-api.d.ts +7 -2
  180. package/dist/dashboard/vc-consumer-profiles-api.d.ts.map +1 -1
  181. package/dist/dashboard/vc-consumer-profiles-api.js +13 -0
  182. package/dist/dashboard/vc-consumer-profiles-api.js.map +1 -1
  183. package/dist/dashboard/web/app.d.ts.map +1 -1
  184. package/dist/dashboard/web/app.js +18 -5
  185. package/dist/dashboard/web/app.js.map +1 -1
  186. package/dist/dashboard/web/bot-defaults-page.d.ts +16 -0
  187. package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -1
  188. package/dist/dashboard/web/bot-defaults-page.js +288 -7
  189. package/dist/dashboard/web/bot-defaults-page.js.map +1 -1
  190. package/dist/dashboard/web/bot-defaults.d.ts +10 -0
  191. package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
  192. package/dist/dashboard/web/bot-defaults.js.map +1 -1
  193. package/dist/dashboard/web/client-shell.d.ts +29 -0
  194. package/dist/dashboard/web/client-shell.d.ts.map +1 -0
  195. package/dist/dashboard/web/client-shell.js +89 -0
  196. package/dist/dashboard/web/client-shell.js.map +1 -0
  197. package/dist/dashboard/web/connectors-page.d.ts.map +1 -1
  198. package/dist/dashboard/web/connectors-page.js +5 -1
  199. package/dist/dashboard/web/connectors-page.js.map +1 -1
  200. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  201. package/dist/dashboard/web/i18n.js +130 -22
  202. package/dist/dashboard/web/i18n.js.map +1 -1
  203. package/dist/dashboard/web/preferences.d.ts +7 -0
  204. package/dist/dashboard/web/preferences.d.ts.map +1 -1
  205. package/dist/dashboard/web/preferences.js +32 -0
  206. package/dist/dashboard/web/preferences.js.map +1 -1
  207. package/dist/dashboard/web/sessions-kanban.d.ts +1 -1
  208. package/dist/dashboard/web/sessions-kanban.d.ts.map +1 -1
  209. package/dist/dashboard/web/sessions-kanban.js +1 -1
  210. package/dist/dashboard/web/sessions-kanban.js.map +1 -1
  211. package/dist/dashboard/web/sessions-page.d.ts.map +1 -1
  212. package/dist/dashboard/web/sessions-page.js +78 -67
  213. package/dist/dashboard/web/sessions-page.js.map +1 -1
  214. package/dist/dashboard/web/sessions.d.ts +4 -0
  215. package/dist/dashboard/web/sessions.d.ts.map +1 -1
  216. package/dist/dashboard/web/sessions.js +14 -1
  217. package/dist/dashboard/web/sessions.js.map +1 -1
  218. package/dist/dashboard/web/settings-page.d.ts +6 -0
  219. package/dist/dashboard/web/settings-page.d.ts.map +1 -1
  220. package/dist/dashboard/web/settings-page.js +55 -30
  221. package/dist/dashboard/web/settings-page.js.map +1 -1
  222. package/dist/dashboard/web/store.d.ts +1 -2
  223. package/dist/dashboard/web/store.d.ts.map +1 -1
  224. package/dist/dashboard/web/store.js +69 -20
  225. package/dist/dashboard/web/store.js.map +1 -1
  226. package/dist/dashboard/web/vc-consumer-profiles-section.d.ts.map +1 -1
  227. package/dist/dashboard/web/vc-consumer-profiles-section.js +128 -42
  228. package/dist/dashboard/web/vc-consumer-profiles-section.js.map +1 -1
  229. package/dist/dashboard/webhook-routes.d.ts.map +1 -1
  230. package/dist/dashboard/webhook-routes.js +9 -2
  231. package/dist/dashboard/webhook-routes.js.map +1 -1
  232. package/dist/dashboard-web/app.js +1 -1
  233. package/dist/dashboard-web/chunks/bot-defaults-page-VQMPUJ4Y.js +10 -0
  234. package/dist/dashboard-web/chunks/chunk-AGCPF6MT.js +1 -0
  235. package/dist/dashboard-web/chunks/{chunk-QCPPKCX3.js → chunk-JMRGZQDB.js} +1 -1
  236. package/dist/dashboard-web/chunks/{chunk-EJDYUINZ.js → chunk-NP4JUBGS.js} +1 -1
  237. package/dist/dashboard-web/chunks/{chunk-WXPMNEYV.js → chunk-P327LXOT.js} +1 -1
  238. package/dist/dashboard-web/chunks/chunk-SNKATZWK.js +49 -0
  239. package/dist/dashboard-web/chunks/connectors-page-DOY4J3ZL.js +1 -0
  240. package/dist/dashboard-web/chunks/{groups-page-3P4TLUMF.js → groups-page-74TS2DIH.js} +1 -1
  241. package/dist/dashboard-web/chunks/{insights-page-TDCZV56A.js → insights-page-AYCO65JC.js} +1 -1
  242. package/dist/dashboard-web/chunks/{monitor-room-QWCWL3SX.js → monitor-room-YKYLTSAN.js} +1 -1
  243. package/dist/dashboard-web/chunks/{monitoring-page-IOWFSNVC.js → monitoring-page-QBIUNRBR.js} +1 -1
  244. package/dist/dashboard-web/chunks/{office-page-5AUPEHZP.js → office-page-4PH2A26P.js} +1 -1
  245. package/dist/dashboard-web/chunks/overview-page-H73D5UT4.js +1 -0
  246. package/dist/dashboard-web/chunks/{roles-page-WJVB52ET.js → roles-page-U4BTOQ7F.js} +1 -1
  247. package/dist/dashboard-web/chunks/{schedules-page-RVRWHP4V.js → schedules-page-RHRHX7R7.js} +1 -1
  248. package/dist/dashboard-web/chunks/sessions-page-VSC6AM5G.js +2 -0
  249. package/dist/dashboard-web/chunks/settings-page-LF3RKM5P.js +2 -0
  250. package/dist/dashboard-web/chunks/{skills-page-7YT5TIX5.js → skills-page-SYOIGGXH.js} +1 -1
  251. package/dist/dashboard-web/chunks/{team-federation-page-NJZKTU7A.js → team-federation-page-RMBLASTW.js} +1 -1
  252. package/dist/dashboard-web/chunks/{v3-page-NQ73UA2Q.js → v3-page-XJXKKJRN.js} +1 -1
  253. package/dist/dashboard-web/chunks/{whiteboards-page-DSM3MDMN.js → whiteboards-page-K3YVSTHJ.js} +1 -1
  254. package/dist/dashboard-web/style.css +571 -3
  255. package/dist/dashboard.js +238 -9
  256. package/dist/dashboard.js.map +1 -1
  257. package/dist/global-config.d.ts +11 -0
  258. package/dist/global-config.d.ts.map +1 -1
  259. package/dist/global-config.js +27 -1
  260. package/dist/global-config.js.map +1 -1
  261. package/dist/i18n/en.d.ts.map +1 -1
  262. package/dist/i18n/en.js +6 -19
  263. package/dist/i18n/en.js.map +1 -1
  264. package/dist/i18n/zh.d.ts.map +1 -1
  265. package/dist/i18n/zh.js +6 -19
  266. package/dist/i18n/zh.js.map +1 -1
  267. package/dist/im/lark/card-builder.d.ts +1 -14
  268. package/dist/im/lark/card-builder.d.ts.map +1 -1
  269. package/dist/im/lark/card-builder.js +1 -35
  270. package/dist/im/lark/card-builder.js.map +1 -1
  271. package/dist/im/lark/card-handler.d.ts +10 -0
  272. package/dist/im/lark/card-handler.d.ts.map +1 -1
  273. package/dist/im/lark/card-handler.js +161 -44
  274. package/dist/im/lark/card-handler.js.map +1 -1
  275. package/dist/im/lark/client.d.ts +47 -0
  276. package/dist/im/lark/client.d.ts.map +1 -1
  277. package/dist/im/lark/client.js +127 -18
  278. package/dist/im/lark/client.js.map +1 -1
  279. package/dist/im/lark/event-dispatcher.d.ts +13 -0
  280. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  281. package/dist/im/lark/event-dispatcher.js +48 -3
  282. package/dist/im/lark/event-dispatcher.js.map +1 -1
  283. package/dist/im/lark/identity-cache.d.ts +25 -1
  284. package/dist/im/lark/identity-cache.d.ts.map +1 -1
  285. package/dist/im/lark/identity-cache.js +47 -1
  286. package/dist/im/lark/identity-cache.js.map +1 -1
  287. package/dist/im/lark/overload-nonce.d.ts +19 -0
  288. package/dist/im/lark/overload-nonce.d.ts.map +1 -0
  289. package/dist/im/lark/overload-nonce.js +72 -0
  290. package/dist/im/lark/overload-nonce.js.map +1 -0
  291. package/dist/im/lark/v3-progress-card.d.ts.map +1 -1
  292. package/dist/im/lark/v3-progress-card.js +5 -0
  293. package/dist/im/lark/v3-progress-card.js.map +1 -1
  294. package/dist/services/async-trigger-state.d.ts +82 -0
  295. package/dist/services/async-trigger-state.d.ts.map +1 -0
  296. package/dist/services/async-trigger-state.js +95 -0
  297. package/dist/services/async-trigger-state.js.map +1 -0
  298. package/dist/services/async-trigger-store.d.ts +28 -0
  299. package/dist/services/async-trigger-store.d.ts.map +1 -0
  300. package/dist/services/async-trigger-store.js +120 -0
  301. package/dist/services/async-trigger-store.js.map +1 -0
  302. package/dist/services/bot-config-store.d.ts.map +1 -1
  303. package/dist/services/bot-config-store.js +34 -2
  304. package/dist/services/bot-config-store.js.map +1 -1
  305. package/dist/services/bridge-fallback-gate.d.ts +32 -0
  306. package/dist/services/bridge-fallback-gate.d.ts.map +1 -1
  307. package/dist/services/bridge-fallback-gate.js +39 -0
  308. package/dist/services/bridge-fallback-gate.js.map +1 -1
  309. package/dist/services/bridge-turn-queue.d.ts.map +1 -1
  310. package/dist/services/bridge-turn-queue.js +15 -1
  311. package/dist/services/bridge-turn-queue.js.map +1 -1
  312. package/dist/services/card-prefs-store.d.ts +4 -0
  313. package/dist/services/card-prefs-store.d.ts.map +1 -1
  314. package/dist/services/card-prefs-store.js +8 -0
  315. package/dist/services/card-prefs-store.js.map +1 -1
  316. package/dist/services/claude-transcript.d.ts +23 -0
  317. package/dist/services/claude-transcript.d.ts.map +1 -1
  318. package/dist/services/claude-transcript.js +33 -0
  319. package/dist/services/claude-transcript.js.map +1 -1
  320. package/dist/services/codex-app-runner-protocol.d.ts +61 -0
  321. package/dist/services/codex-app-runner-protocol.d.ts.map +1 -0
  322. package/dist/services/codex-app-runner-protocol.js +170 -0
  323. package/dist/services/codex-app-runner-protocol.js.map +1 -0
  324. package/dist/services/codex-app-turn-controller.d.ts +72 -0
  325. package/dist/services/codex-app-turn-controller.d.ts.map +1 -0
  326. package/dist/services/codex-app-turn-controller.js +524 -0
  327. package/dist/services/codex-app-turn-controller.js.map +1 -0
  328. package/dist/services/codex-runner-freshness.d.ts +39 -0
  329. package/dist/services/codex-runner-freshness.d.ts.map +1 -0
  330. package/dist/services/codex-runner-freshness.js +78 -0
  331. package/dist/services/codex-runner-freshness.js.map +1 -0
  332. package/dist/services/connector-store.d.ts +6 -0
  333. package/dist/services/connector-store.d.ts.map +1 -1
  334. package/dist/services/connector-store.js.map +1 -1
  335. package/dist/services/grant-store.d.ts.map +1 -1
  336. package/dist/services/grant-store.js +16 -0
  337. package/dist/services/grant-store.js.map +1 -1
  338. package/dist/services/hermes-transcript.d.ts +1 -0
  339. package/dist/services/hermes-transcript.d.ts.map +1 -1
  340. package/dist/services/hermes-transcript.js +49 -0
  341. package/dist/services/hermes-transcript.js.map +1 -1
  342. package/dist/services/sandbox-migration.d.ts +14 -0
  343. package/dist/services/sandbox-migration.d.ts.map +1 -0
  344. package/dist/services/sandbox-migration.js +85 -0
  345. package/dist/services/sandbox-migration.js.map +1 -0
  346. package/dist/services/sandbox-store.d.ts +28 -0
  347. package/dist/services/sandbox-store.d.ts.map +1 -1
  348. package/dist/services/sandbox-store.js +82 -0
  349. package/dist/services/sandbox-store.js.map +1 -1
  350. package/dist/services/schedule-split-migration.d.ts +2 -0
  351. package/dist/services/schedule-split-migration.d.ts.map +1 -0
  352. package/dist/services/schedule-split-migration.js +159 -0
  353. package/dist/services/schedule-split-migration.js.map +1 -0
  354. package/dist/services/schedule-store.d.ts +28 -4
  355. package/dist/services/schedule-store.d.ts.map +1 -1
  356. package/dist/services/schedule-store.js +118 -33
  357. package/dist/services/schedule-store.js.map +1 -1
  358. package/dist/services/trigger-types.d.ts +18 -1
  359. package/dist/services/trigger-types.d.ts.map +1 -1
  360. package/dist/services/trigger-types.js +3 -0
  361. package/dist/services/trigger-types.js.map +1 -1
  362. package/dist/services/vc-meeting-consumer-profile-store.d.ts.map +1 -1
  363. package/dist/services/vc-meeting-consumer-profile-store.js +1 -0
  364. package/dist/services/vc-meeting-consumer-profile-store.js.map +1 -1
  365. package/dist/services/vc-meeting-consumer-profile-templates.d.ts +32 -0
  366. package/dist/services/vc-meeting-consumer-profile-templates.d.ts.map +1 -0
  367. package/dist/services/vc-meeting-consumer-profile-templates.js +106 -0
  368. package/dist/services/vc-meeting-consumer-profile-templates.js.map +1 -0
  369. package/dist/services/vc-meeting-delivery-feed.d.ts +1 -1
  370. package/dist/services/vc-meeting-delivery-feed.d.ts.map +1 -1
  371. package/dist/services/vc-meeting-delivery-feed.js +2 -1
  372. package/dist/services/vc-meeting-delivery-feed.js.map +1 -1
  373. package/dist/services/vc-meeting-delivery-hub-store.d.ts +3 -1
  374. package/dist/services/vc-meeting-delivery-hub-store.d.ts.map +1 -1
  375. package/dist/services/vc-meeting-delivery-hub-store.js +11 -0
  376. package/dist/services/vc-meeting-delivery-hub-store.js.map +1 -1
  377. package/dist/services/vc-meeting-delivery-protocol.d.ts +9 -2
  378. package/dist/services/vc-meeting-delivery-protocol.d.ts.map +1 -1
  379. package/dist/services/vc-meeting-delivery-protocol.js +10 -1
  380. package/dist/services/vc-meeting-delivery-protocol.js.map +1 -1
  381. package/dist/services/vc-meeting-delivery-receiver.d.ts.map +1 -1
  382. package/dist/services/vc-meeting-delivery-receiver.js +10 -1
  383. package/dist/services/vc-meeting-delivery-receiver.js.map +1 -1
  384. package/dist/services/vc-meeting-delivery-store.d.ts +12 -2
  385. package/dist/services/vc-meeting-delivery-store.d.ts.map +1 -1
  386. package/dist/services/vc-meeting-delivery-store.js +14 -2
  387. package/dist/services/vc-meeting-delivery-store.js.map +1 -1
  388. package/dist/services/vc-meeting-im-reply.d.ts +2 -1
  389. package/dist/services/vc-meeting-im-reply.d.ts.map +1 -1
  390. package/dist/services/vc-meeting-im-reply.js +19 -3
  391. package/dist/services/vc-meeting-im-reply.js.map +1 -1
  392. package/dist/services/vc-meeting-listener-output-protocol.d.ts +27 -0
  393. package/dist/services/vc-meeting-listener-output-protocol.d.ts.map +1 -0
  394. package/dist/services/vc-meeting-listener-output-protocol.js +58 -0
  395. package/dist/services/vc-meeting-listener-output-protocol.js.map +1 -0
  396. package/dist/services/vc-meeting-listener-topic-store.d.ts +31 -0
  397. package/dist/services/vc-meeting-listener-topic-store.d.ts.map +1 -0
  398. package/dist/services/vc-meeting-listener-topic-store.js +120 -0
  399. package/dist/services/vc-meeting-listener-topic-store.js.map +1 -0
  400. package/dist/services/vc-meeting-runtime-store.d.ts +2 -1
  401. package/dist/services/vc-meeting-runtime-store.d.ts.map +1 -1
  402. package/dist/services/vc-meeting-runtime-store.js +8 -0
  403. package/dist/services/vc-meeting-runtime-store.js.map +1 -1
  404. package/dist/services/vc-meeting-send-policy.d.ts +8 -1
  405. package/dist/services/vc-meeting-send-policy.d.ts.map +1 -1
  406. package/dist/services/vc-meeting-send-policy.js +8 -0
  407. package/dist/services/vc-meeting-send-policy.js.map +1 -1
  408. package/dist/types.d.ts +53 -4
  409. package/dist/types.d.ts.map +1 -1
  410. package/dist/utils/allowed-users-apply.d.ts +76 -0
  411. package/dist/utils/allowed-users-apply.d.ts.map +1 -0
  412. package/dist/utils/allowed-users-apply.js +123 -0
  413. package/dist/utils/allowed-users-apply.js.map +1 -0
  414. package/dist/utils/allowed-users-cache.d.ts +29 -0
  415. package/dist/utils/allowed-users-cache.d.ts.map +1 -0
  416. package/dist/utils/allowed-users-cache.js +82 -0
  417. package/dist/utils/allowed-users-cache.js.map +1 -0
  418. package/dist/utils/child-env.d.ts +4 -3
  419. package/dist/utils/child-env.d.ts.map +1 -1
  420. package/dist/utils/child-env.js +16 -1
  421. package/dist/utils/child-env.js.map +1 -1
  422. package/dist/utils/cli-usage-limit.d.ts +27 -1
  423. package/dist/utils/cli-usage-limit.d.ts.map +1 -1
  424. package/dist/utils/cli-usage-limit.js +92 -20
  425. package/dist/utils/cli-usage-limit.js.map +1 -1
  426. package/dist/utils/input-gate.d.ts +2 -0
  427. package/dist/utils/input-gate.d.ts.map +1 -1
  428. package/dist/utils/input-gate.js +2 -0
  429. package/dist/utils/input-gate.js.map +1 -1
  430. package/dist/utils/runtime-build-id.d.ts +21 -0
  431. package/dist/utils/runtime-build-id.d.ts.map +1 -0
  432. package/dist/utils/runtime-build-id.js +83 -0
  433. package/dist/utils/runtime-build-id.js.map +1 -0
  434. package/dist/utils/terminal-renderer.d.ts +3 -3
  435. package/dist/utils/terminal-renderer.js +3 -3
  436. package/dist/vc-agent/cards.d.ts.map +1 -1
  437. package/dist/vc-agent/cards.js +21 -1
  438. package/dist/vc-agent/cards.js.map +1 -1
  439. package/dist/worker.js +1010 -669
  440. package/dist/worker.js.map +1 -1
  441. package/dist/workflows/events/payloads.d.ts +56 -0
  442. package/dist/workflows/events/payloads.d.ts.map +1 -1
  443. package/dist/workflows/events/payloads.js +9 -0
  444. package/dist/workflows/events/payloads.js.map +1 -1
  445. package/dist/workflows/events/schema.d.ts +86 -0
  446. package/dist/workflows/events/schema.d.ts.map +1 -1
  447. package/dist/workflows/hostExecutors/botmux-schedule.d.ts.map +1 -1
  448. package/dist/workflows/hostExecutors/botmux-schedule.js +13 -1
  449. package/dist/workflows/hostExecutors/botmux-schedule.js.map +1 -1
  450. package/dist/workflows/migration/v2-run-archive-schema.d.ts +20 -20
  451. package/dist/workflows/migration/v2-to-v3.d.ts.map +1 -1
  452. package/dist/workflows/migration/v2-to-v3.js +5 -1
  453. package/dist/workflows/migration/v2-to-v3.js.map +1 -1
  454. package/dist/workflows/shared/sandbox-policy.d.ts +10 -1
  455. package/dist/workflows/shared/sandbox-policy.d.ts.map +1 -1
  456. package/dist/workflows/shared/sandbox-policy.js +12 -0
  457. package/dist/workflows/shared/sandbox-policy.js.map +1 -1
  458. package/dist/workflows/v3/architect.d.ts.map +1 -1
  459. package/dist/workflows/v3/architect.js +1 -0
  460. package/dist/workflows/v3/architect.js.map +1 -1
  461. package/dist/workflows/v3/bot-resolve.d.ts.map +1 -1
  462. package/dist/workflows/v3/bot-resolve.js +36 -0
  463. package/dist/workflows/v3/bot-resolve.js.map +1 -1
  464. package/dist/workflows/v3/cli-run.d.ts +9 -0
  465. package/dist/workflows/v3/cli-run.d.ts.map +1 -1
  466. package/dist/workflows/v3/cli-run.js +17 -2
  467. package/dist/workflows/v3/cli-run.js.map +1 -1
  468. package/dist/workflows/v3/contract.d.ts +8 -0
  469. package/dist/workflows/v3/contract.d.ts.map +1 -1
  470. package/dist/workflows/v3/contract.js.map +1 -1
  471. package/dist/workflows/v3/goal-cli.d.ts +58 -0
  472. package/dist/workflows/v3/goal-cli.d.ts.map +1 -0
  473. package/dist/workflows/v3/goal-cli.js +430 -0
  474. package/dist/workflows/v3/goal-cli.js.map +1 -0
  475. package/dist/workflows/v3/library-materialize.d.ts.map +1 -1
  476. package/dist/workflows/v3/library-materialize.js +5 -1
  477. package/dist/workflows/v3/library-materialize.js.map +1 -1
  478. package/dist/workflows/v3/library-schema.d.ts +21 -0
  479. package/dist/workflows/v3/library-schema.d.ts.map +1 -1
  480. package/dist/workflows/v3/library-schema.js +95 -1
  481. package/dist/workflows/v3/library-schema.js.map +1 -1
  482. package/dist/workflows/v3/progress-projection.d.ts +3 -0
  483. package/dist/workflows/v3/progress-projection.d.ts.map +1 -1
  484. package/dist/workflows/v3/progress-projection.js +23 -1
  485. package/dist/workflows/v3/progress-projection.js.map +1 -1
  486. package/dist/workflows/v3/run-envelope.d.ts +46 -1
  487. package/dist/workflows/v3/run-envelope.d.ts.map +1 -1
  488. package/dist/workflows/v3/run-envelope.js +32 -1
  489. package/dist/workflows/v3/run-envelope.js.map +1 -1
  490. package/package.json +2 -2
  491. package/dist/dashboard-web/chunks/bot-defaults-page-O376WSER.js +0 -5
  492. package/dist/dashboard-web/chunks/chunk-H7P5UV4W.js +0 -49
  493. package/dist/dashboard-web/chunks/chunk-SZOB6VK2.js +0 -1
  494. package/dist/dashboard-web/chunks/connectors-page-IR54AOAI.js +0 -1
  495. package/dist/dashboard-web/chunks/overview-page-QJMI6NLW.js +0 -1
  496. package/dist/dashboard-web/chunks/sessions-page-KWLGBX6G.js +0 -3
  497. package/dist/dashboard-web/chunks/settings-page-TNZEPC7V.js +0 -2
  498. package/dist/services/sandbox-land.d.ts +0 -45
  499. package/dist/services/sandbox-land.d.ts.map +0 -1
  500. package/dist/services/sandbox-land.js +0 -394
  501. package/dist/services/sandbox-land.js.map +0 -1
  502. package/dist/utils/screen-analyzer.d.ts +0 -75
  503. package/dist/utils/screen-analyzer.d.ts.map +0 -1
  504. package/dist/utils/screen-analyzer.js +0 -333
  505. package/dist/utils/screen-analyzer.js.map +0 -1
@@ -16,6 +16,7 @@ import { hookCommandFor } from '../adapters/hook-command.js';
16
16
  import { randomBytes, randomUUID } from 'node:crypto';
17
17
  import { config } from '../config.js';
18
18
  import * as sessionStore from '../services/session-store.js';
19
+ import * as asyncTriggerStore from '../services/async-trigger-store.js';
19
20
  import { persistStreamCardState, rememberLastCliInput } from './session-manager.js';
20
21
  import { fallbackTurnId, isSubstituteTurn } from './reply-target.js';
21
22
  import { updateMessage, deleteMessage, sendEphemeralCard, sendUserMessage, addReaction, removeReaction, getMessageChatId, MessageWithdrawnError } from '../im/lark/client.js';
@@ -35,12 +36,15 @@ import { listDocSubscriptionsForSession, removeDocSubscription } from '../servic
35
36
  import { TmuxBackend } from '../adapters/backend/tmux-backend.js';
36
37
  import { HerdrBackend } from '../adapters/backend/herdr-backend.js';
37
38
  import { sandboxEnabled } from '../adapters/backend/sandbox.js';
38
- import { isSuspendableBackendType, getSessionPersistentBackendType, persistentBackendTargetForSession, killPersistentBackendTarget, managedTargetsForCliChange, resolvePairedSpawnBackendType, resolvePersistentBackendTarget } from './persistent-backend.js';
39
+ import { isSuspendableBackendType, getSessionPersistentBackendType, persistentBackendTargetForSession, killPersistentBackendTarget, probePersistentBackendTarget, managedTargetsForCliChange, resolvePairedSpawnBackendType, resolvePersistentBackendTarget } from './persistent-backend.js';
39
40
  import { getBot, getAllBots, resolveBrandLabel } from '../bot-registry.js';
41
+ import { RestartCoordinator } from './restart-coordinator.js';
42
+ import { runtimeBuildIdentity } from '../utils/runtime-build-id.js';
40
43
  /** A random id minted once per daemon process (this lifetime). Stamped onto
41
44
  * isolated persistent panes so a suspend→resume reattach (same id) is
42
45
  * distinguishable from a pane surviving a daemon restart (different id). */
43
46
  const DAEMON_BOOT_ID = randomUUID();
47
+ const restartCoordinator = new RestartCoordinator();
44
48
  export function getDaemonBootId() {
45
49
  return DAEMON_BOOT_ID;
46
50
  }
@@ -76,8 +80,11 @@ import { evaluateVcMeetingManagedSend, resolveVcMeetingImTurnOrigin, } from '../
76
80
  import { finishVcMeetingImReply, prepareVcMeetingDeliveryReply, prepareVcMeetingImReply, } from '../services/vc-meeting-im-reply.js';
77
81
  import { neutralizeLarkAtTags } from '../services/send-policy.js';
78
82
  import { recordVcMeetingListenerMessage } from '../services/vc-meeting-listener-message-store.js';
83
+ import { getVcMeetingListenerTopicRoot, recordVcMeetingListenerTopicRoot, } from '../services/vc-meeting-listener-topic-store.js';
84
+ import { parseVcMeetingListenerOutput } from '../services/vc-meeting-listener-output-protocol.js';
79
85
  import { isLocalCliOpenEnabled, isLocalCliOpenReady } from '../services/local-cli-opener.js';
80
86
  import { isSilentScheduledTurn } from './silent-schedule-turns.js';
87
+ import { isTriggerFinalSuppressed } from './trigger-final-suppression.js';
81
88
  import { writeDeferredTopicBinding } from './deferred-topic-binding.js';
82
89
  import { deferWorkerSpawnDuringDeviceIsolation } from './device-isolation-activation.js';
83
90
  import { buildBotmuxLarkNativeSessionTitle, extractBotmuxLarkNativeSessionTitlePrompt, } from './session-title.js';
@@ -94,6 +101,12 @@ function workerForkEnv(base) {
94
101
  delete env[key];
95
102
  return env;
96
103
  }
104
+ /** Fresh workers always start with hidden output, so restore the daemon-owned mode. */
105
+ function syncWorkerDisplayMode(ds) {
106
+ if (!ds.worker || !ds.displayMode || ds.displayMode === 'hidden')
107
+ return;
108
+ ds.worker.send({ type: 'set_display_mode', mode: ds.displayMode });
109
+ }
97
110
  let callbacks;
98
111
  /**
99
112
  * Initialise worker-pool callbacks. Must be called once before forkWorker().
@@ -1089,6 +1102,7 @@ export function ensureClaudeFolderTrust(workingDir, stateJsonPath = join(homedir
1089
1102
  }
1090
1103
  // ─── Kill worker ────────────────────────────────────────────────────────────
1091
1104
  export function killWorker(ds) {
1105
+ restartCoordinator.cancelSession(ds.session.sessionId);
1092
1106
  clearUsageLimitState(ds);
1093
1107
  ds.localProcessAttestation = undefined;
1094
1108
  // A managed-turn capability belongs to one concrete worker generation.
@@ -1126,6 +1140,111 @@ export function killWorker(ds) {
1126
1140
  ds.workerToken = null;
1127
1141
  ds.workerViewToken = null;
1128
1142
  }
1143
+ /**
1144
+ * Whether a worker-less restart must first destroy the session's persistent
1145
+ * backing pane. Adopt sessions are excluded — botmux never owned the user's
1146
+ * pane, so killing it would violate the bridge invariant. Pure so the
1147
+ * adopt-skip decision is unit-testable without spawning a worker.
1148
+ */
1149
+ export function shouldDestroyPaneBeforeRestart(ds) {
1150
+ return !ds.initConfig?.adoptMode && !ds.adoptedFrom;
1151
+ }
1152
+ /**
1153
+ * Destroy a still-alive persistent backing pane (tmux/herdr/zellij) before a
1154
+ * worker-less restart forks a fresh worker. Without this, a session that lost
1155
+ * its worker but kept its pane (the normal post-daemon-restart state) would let
1156
+ * spawnCli REATTACH the surviving CLI instead of relaunching it — the CLI is
1157
+ * never actually restarted, yet prompt-ready still fires `restart_result:
1158
+ * succeeded`, so the user sees "restarted" while a wedged CLI stays wedged.
1159
+ * Killing the pane first forces a genuine physical fresh spawn, making the
1160
+ * success receipt truthful.
1161
+ *
1162
+ * Fail-safe (codex 复审观察): the kill primitives swallow their own failures
1163
+ * (TmuxBackend.killSession `catch {}`, Herdr `runHerdr` returns false), so a
1164
+ * plain try/catch here can NEVER observe a failed kill — a wedged tmux server
1165
+ * could leave the pane alive and the fork would silently reattach. So we PROBE
1166
+ * after killing and retry once if the pane survives, escalating to a loud warn
1167
+ * when it still exists. A surviving pane still forks (refusing would strand the
1168
+ * session with no restart at all, strictly worse than the original bug), but
1169
+ * the warn makes the rare failure diagnosable instead of a silent false
1170
+ * success. A fully reattach-proof path (forceFresh signal into spawnCli) is a
1171
+ * larger, separate change — tracked as a follow-up, not blocking this fix.
1172
+ *
1173
+ * Scope: persistent panes only (getSessionPersistentBackendType excludes riff,
1174
+ * which never reattaches — it always builds a fresh RiffBackend — and whose
1175
+ * remote task must survive a restart to preserve follow-up lineage). Adopt
1176
+ * sessions are skipped: botmux never owned the user's pane.
1177
+ */
1178
+ function destroyLivePaneBeforeRestart(ds) {
1179
+ if (!shouldDestroyPaneBeforeRestart(ds))
1180
+ return;
1181
+ const target = persistentBackendTargetForSession(ds);
1182
+ if (!target)
1183
+ return;
1184
+ const killOnce = () => {
1185
+ try {
1186
+ killPersistentBackendTarget(target);
1187
+ }
1188
+ catch (err) {
1189
+ // The primitives normally swallow their own errors; this only catches a
1190
+ // truly unexpected throw (e.g. target resolution). Non-fatal — the probe
1191
+ // below is the real signal.
1192
+ logger.warn(`[${tag(ds)}] restart: kill of ${target.backendType} pane threw: ${err}`);
1193
+ }
1194
+ };
1195
+ killOnce();
1196
+ // Advance a single probe result monotonically through the real retry path so
1197
+ // an 'unknown' first probe is never mislabelled as a post-retry survivor:
1198
+ // - 'missing' → gone, fork is genuinely fresh.
1199
+ // - 'unknown' → probe indeterminate (e.g. tmux server hiccup); do NOT retry
1200
+ // and do NOT block the restart — pre-fix always forked, stranding is worse.
1201
+ // - 'exists' → confirmed alive: warn, kill once more, re-probe. Only this
1202
+ // branch performs (and can report) a retry.
1203
+ let probe = probePersistentBackendTarget(target);
1204
+ if (probe === 'exists') {
1205
+ logger.warn(`[${tag(ds)}] restart: ${target.backendType} pane survived first kill — retrying before refork`);
1206
+ killOnce();
1207
+ probe = probePersistentBackendTarget(target);
1208
+ }
1209
+ if (probe === 'exists') {
1210
+ // The kill genuinely failed (twice). Forking will likely reattach the live
1211
+ // pane (the very bug this guards), so the eventual `restart_result:
1212
+ // succeeded` may again be untruthful — but leave a loud, greppable trail.
1213
+ logger.error(`[${tag(ds)}] restart: ${target.backendType} pane STILL alive after retry — `
1214
+ + 'the refork may reattach instead of relaunching (restart success may be untruthful)');
1215
+ }
1216
+ else if (probe === 'unknown') {
1217
+ // Do NOT over-promise a relaunch: an indeterminate probe could still be a
1218
+ // live pane the fork reattaches. Fork proceeds (stranding is worse) but the
1219
+ // diagnostic must not claim a fresh relaunch it can't guarantee.
1220
+ logger.warn(`[${tag(ds)}] restart: ${target.backendType} kill outcome indeterminate — `
1221
+ + 'refork may reattach instead of relaunching');
1222
+ }
1223
+ else {
1224
+ logger.info(`[${tag(ds)}] restart: ${target.backendType} pane missing after kill — CLI will physically relaunch`);
1225
+ }
1226
+ }
1227
+ /** Join or start one correlated physical restart for a session. */
1228
+ export function requestSessionRestart(ds, observer) {
1229
+ return restartCoordinator.request(ds.session.sessionId, observer, attemptId => {
1230
+ if (ds.worker && !ds.worker.killed) {
1231
+ ds.worker.send({ type: 'restart', attemptId });
1232
+ return;
1233
+ }
1234
+ // No live worker but the persistent pane may still be alive (e.g. after a
1235
+ // daemon restart). Tear it down first so forkWorker → spawnCli spawns a
1236
+ // fresh CLI instead of reattaching the old one and falsely reporting a
1237
+ // successful restart.
1238
+ destroyLivePaneBeforeRestart(ds);
1239
+ forkWorker(ds, '', {
1240
+ resume: ds.hasHistory,
1241
+ restartAttemptId: attemptId,
1242
+ });
1243
+ });
1244
+ }
1245
+ export function __testOnly_resetRestartCoordinator() {
1246
+ restartCoordinator.reset();
1247
+ }
1129
1248
  /**
1130
1249
  * Tear down a persistent backing session (tmux/herdr/zellij) directly from the
1131
1250
  * daemon when there is no live worker to do it via the 'close' IPC. The session
@@ -1299,26 +1418,21 @@ export async function closeSession(sessionId) {
1299
1418
  // crash/limited turn may never have reached an idle edge).
1300
1419
  recordUsageForDaemonSession(ds);
1301
1420
  killWorker(ds);
1302
- // 文档入口清理:会话关闭即删除其绑定。只有旧
1303
- // /subscribe-lark-doc 记录需要调飞书逐文件退订 API;
1304
- // /watch-comment 仅依赖应用级评论事件,删本地监听表即可。
1305
- try {
1306
- const anchor = sessionAnchorId(ds);
1307
- const subs = listDocSubscriptionsForSession(config.session.dataDir, ds.larkAppId, anchor);
1308
- for (const sub of subs) {
1309
- if (sub.managedBy !== 'watch-comment') {
1310
- await unsubscribeDocFile(ds.larkAppId, { fileToken: sub.fileToken, fileType: sub.fileType });
1311
- }
1312
- removeDocSubscription(config.session.dataDir, ds.larkAppId, sub.fileToken);
1313
- }
1314
- if (subs.length)
1315
- logger.info(`[doc-comment] session ${sessionId.slice(0, 8)} closed → removed ${subs.length} doc binding(s)`);
1316
- }
1317
- catch (err) {
1318
- logger.warn(`[doc-comment] cleanup on close failed for ${sessionId.slice(0, 8)}: ${err?.message ?? err}`);
1319
- }
1421
+ // Commit the daemon-visible close barrier before any network await below.
1422
+ // `botmux delete` may be running inside the session being destroyed; once
1423
+ // its worker/backing pane exits, a new IM message must not find this stale
1424
+ // entry and resurrect the old logical session while doc cleanup is pending.
1320
1425
  activeSessionsRegistry?.delete(activeSessionKey(ds));
1321
1426
  killedLive = true;
1427
+ }
1428
+ // Persistence is part of the same no-await barrier as Map eviction. If the
1429
+ // daemon crashes during best-effort doc cleanup, restart recovery must not
1430
+ // restore a session that was already explicitly closed.
1431
+ const stored = sessionStore.getSession(sessionId);
1432
+ const wasOpen = !!stored && stored.status !== 'closed';
1433
+ if (wasOpen)
1434
+ sessionStore.closeSession(sessionId);
1435
+ if (ds) {
1322
1436
  if (!ds.exitEventEmitted) {
1323
1437
  ds.exitEventEmitted = true;
1324
1438
  dashboardEventBus.publish({
@@ -1328,11 +1442,10 @@ export async function closeSession(sessionId) {
1328
1442
  emitSessionLifecycleHook(ds, 'session.exit', { reason: 'dashboard_close' });
1329
1443
  }
1330
1444
  }
1331
- // Persistence path load mark closed save (delegated to sessionStore).
1332
- const stored = sessionStore.getSession(sessionId);
1333
- const wasOpen = !!stored && stored.status !== 'closed';
1445
+ // Preserve the existing externally-visible event order: exit first, then
1446
+ // the final persisted-row update. Persistence itself was already committed
1447
+ // above as part of the close barrier.
1334
1448
  if (wasOpen) {
1335
- sessionStore.closeSession(sessionId);
1336
1449
  const after = sessionStore.getSession(sessionId);
1337
1450
  dashboardEventBus.publish({
1338
1451
  type: 'session.update',
@@ -1346,6 +1459,27 @@ export async function closeSession(sessionId) {
1346
1459
  },
1347
1460
  });
1348
1461
  }
1462
+ if (ds) {
1463
+ // 文档入口清理:会话关闭即删除其绑定。只有旧
1464
+ // /subscribe-lark-doc 记录需要调飞书逐文件退订 API;
1465
+ // /watch-comment 仅依赖应用级评论事件,删本地监听表即可。此段允许 await,
1466
+ // 因为上面的内存 + 持久化关闭屏障已经提交。
1467
+ try {
1468
+ const anchor = sessionAnchorId(ds);
1469
+ const subs = listDocSubscriptionsForSession(config.session.dataDir, ds.larkAppId, anchor);
1470
+ for (const sub of subs) {
1471
+ if (sub.managedBy !== 'watch-comment') {
1472
+ await unsubscribeDocFile(ds.larkAppId, { fileToken: sub.fileToken, fileType: sub.fileType });
1473
+ }
1474
+ removeDocSubscription(config.session.dataDir, ds.larkAppId, sub.fileToken);
1475
+ }
1476
+ if (subs.length)
1477
+ logger.info(`[doc-comment] session ${sessionId.slice(0, 8)} closed → removed ${subs.length} doc binding(s)`);
1478
+ }
1479
+ catch (err) {
1480
+ logger.warn(`[doc-comment] cleanup on close failed for ${sessionId.slice(0, 8)}: ${err?.message ?? err}`);
1481
+ }
1482
+ }
1349
1483
  // alreadyClosed = nothing happened on either path.
1350
1484
  const alreadyClosed = !killedLive && !wasOpen;
1351
1485
  return { ok: true, alreadyClosed };
@@ -1707,6 +1841,7 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
1707
1841
  let resume = false;
1708
1842
  let initTurnId;
1709
1843
  let initDispatchAttempt;
1844
+ let restartAttemptId;
1710
1845
  if (typeof resumeOrTurnId === 'string') {
1711
1846
  initTurnId = resumeOrTurnId;
1712
1847
  }
@@ -1714,6 +1849,7 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
1714
1849
  resume = resumeOrTurnId.resume === true;
1715
1850
  initTurnId = resumeOrTurnId.turnId;
1716
1851
  initDispatchAttempt = resumeOrTurnId.dispatchAttempt;
1852
+ restartAttemptId = resumeOrTurnId.restartAttemptId;
1717
1853
  }
1718
1854
  else {
1719
1855
  resume = resumeOrTurnId;
@@ -1766,6 +1902,7 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
1766
1902
  if (ds.session.sandbox === undefined) {
1767
1903
  if (!resume) {
1768
1904
  ds.session.sandbox = botCfg.sandbox === true;
1905
+ ds.session.sandboxPaths = botCfg.sandboxPaths;
1769
1906
  ds.session.sandboxHidePaths = botCfg.sandboxHidePaths ?? [];
1770
1907
  ds.session.sandboxReadonlyPaths = botCfg.sandboxReadonlyPaths ?? [];
1771
1908
  ds.session.sandboxNetwork = botCfg.sandboxNetwork !== false;
@@ -1926,6 +2063,7 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
1926
2063
  });
1927
2064
  // Send init config — use per-bot settings
1928
2065
  const promptCodexAppInput = codexAppInputForSession(ds, promptPayload.codexAppInput, initAttributionTurnId);
2066
+ const runtimeIdentity = runtimeBuildIdentity();
1929
2067
  const initMsg = {
1930
2068
  type: 'init',
1931
2069
  sessionId: ds.session.sessionId,
@@ -1951,6 +2089,7 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
1951
2089
  // Use the decision recorded on the session (above), NOT the live bot flag, so
1952
2090
  // historical sessions never get retroactively sandboxed on restart.
1953
2091
  sandbox: ds.session.sandbox === true,
2092
+ sandboxPaths: ds.session.sandboxPaths ?? botCfg.sandboxPaths,
1954
2093
  sandboxHidePaths: ds.session.sandboxHidePaths ?? [],
1955
2094
  sandboxReadonlyPaths: ds.session.sandboxReadonlyPaths ?? [],
1956
2095
  sandboxNetwork: ds.session.sandboxNetwork !== false,
@@ -1996,6 +2135,11 @@ export function forkWorker(ds, promptInput, resumeOrTurnId = false) {
1996
2135
  vcMeetingImTurnOrigin: resolveVcMeetingImTurnOrigin(ds.session, initAttributionTurnId),
1997
2136
  pluginBindings: botCfg.plugins,
1998
2137
  skillPolicy: botCfg.skills,
2138
+ ...(runtimeIdentity.status === 'known'
2139
+ ? { runnerBuildId: runtimeIdentity.id }
2140
+ : {}),
2141
+ ...(ds.session.runnerBuildId ? { persistedRunnerBuildId: ds.session.runnerBuildId } : {}),
2142
+ ...(restartAttemptId ? { restartAttemptId } : {}),
1999
2143
  };
2000
2144
  worker.send(initMsg);
2001
2145
  ds.initConfig = initMsg;
@@ -2137,6 +2281,8 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
2137
2281
  const managedFinalOutputSuppressed = (turnId, dispatchAttempt) => {
2138
2282
  if (isSilentScheduledTurn(ds, turnId))
2139
2283
  return true;
2284
+ if (isTriggerFinalSuppressed(ds, turnId))
2285
+ return true;
2140
2286
  if (!ds.session.vcMeetingReceiver) {
2141
2287
  return ordinaryManagedSuppression(turnId, dispatchAttempt);
2142
2288
  }
@@ -2307,6 +2453,7 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
2307
2453
  // (if any) is left untouched. The next real user turn clears this flag
2308
2454
  // (rememberLastCliInput) and the normal card flow resumes.
2309
2455
  if (ds.suppressRecoveryCard) {
2456
+ syncWorkerDisplayMode(ds);
2310
2457
  logger.info(`[${t}] Restored session — suppressing recovery streaming card (silent restart)`);
2311
2458
  break;
2312
2459
  }
@@ -2341,9 +2488,7 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
2341
2488
  }
2342
2489
  persistStreamCardState(ds);
2343
2490
  // Re-sync worker's display mode (it starts fresh in 'hidden')
2344
- if (ds.worker && ds.displayMode && ds.displayMode !== 'hidden') {
2345
- ds.worker.send({ type: 'set_display_mode', mode: ds.displayMode });
2346
- }
2491
+ syncWorkerDisplayMode(ds);
2347
2492
  // The restored card is now the active one — withdraw any cards
2348
2493
  // frozen before the daemon went down so they don't pile up in the
2349
2494
  // thread on each restart.
@@ -2393,9 +2538,7 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
2393
2538
  ds.streamCardPending = false;
2394
2539
  persistStreamCardState(ds);
2395
2540
  // Re-sync worker's display mode (it starts fresh in 'hidden')
2396
- if (ds.worker && ds.displayMode && ds.displayMode !== 'hidden') {
2397
- ds.worker.send({ type: 'set_display_mode', mode: ds.displayMode });
2398
- }
2541
+ syncWorkerDisplayMode(ds);
2399
2542
  // New card is live — recall any cards frozen by previous turns.
2400
2543
  // Done after `streamCardId` is committed so we never delete the old
2401
2544
  // card without a successor visible to the user.
@@ -2492,6 +2635,28 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
2492
2635
  invalidateStuckWarning(ds, 'prompt_ready');
2493
2636
  break;
2494
2637
  }
2638
+ case 'runner_build_ready': {
2639
+ const identity = runtimeBuildIdentity();
2640
+ if (ds.worker === worker
2641
+ && effectiveCliId === 'codex-app'
2642
+ && identity.status === 'known'
2643
+ && msg.runnerBuildId === identity.id) {
2644
+ ds.session.runnerBuildId = msg.runnerBuildId;
2645
+ sessionStore.updateSession(ds.session);
2646
+ }
2647
+ else {
2648
+ logger.warn(`[${t}] Ignored invalid or stale runner_build_ready`);
2649
+ }
2650
+ break;
2651
+ }
2652
+ case 'restart_result': {
2653
+ if (ds.worker !== worker) {
2654
+ logger.warn(`[${t}] Ignored restart_result from stale worker generation`);
2655
+ break;
2656
+ }
2657
+ restartCoordinator.resolve(ds.session.sessionId, msg.attemptId, msg.status);
2658
+ break;
2659
+ }
2495
2660
  case 'cli_session_id': {
2496
2661
  const wasLocalCliOpenReady = isLocalCliOpenReady(ds, { cliId: effectiveCliId });
2497
2662
  ds.session.cliSessionId = msg.cliSessionId;
@@ -3111,6 +3276,25 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
3111
3276
  }
3112
3277
  break;
3113
3278
  }
3279
+ case 'steer_accepted': {
3280
+ if (ds.worker !== worker) {
3281
+ logger.warn(`[${t}] Ignored steer_accepted from stale worker generation`);
3282
+ break;
3283
+ }
3284
+ logger.info(`[${t}] Codex App steer accepted `
3285
+ + `appTurn=${msg.appTurnId.slice(0, 12)} replyTurn=${msg.turnId.slice(0, 12)}`);
3286
+ if (managedAuxUiSuppressed(msg.turnId, undefined))
3287
+ break;
3288
+ try {
3289
+ await scopedReply(tr('worker.steer_accepted', undefined, loc), 'text', msg.turnId);
3290
+ }
3291
+ catch {
3292
+ logger.error(`[${t}] Failed to deliver steer acknowledgement `
3293
+ + `appTurn=${msg.appTurnId.slice(0, 12)} replyTurn=${msg.turnId.slice(0, 12)} `
3294
+ + 'category=delivery');
3295
+ }
3296
+ break;
3297
+ }
3114
3298
  case 'turn_terminal': {
3115
3299
  if (ds.worker !== worker) {
3116
3300
  logger.warn(`[${t}] Ignored turn_terminal from stale worker generation`);
@@ -3241,6 +3425,17 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
3241
3425
  // Worker pops the turn off its queue right after emit, so it will
3242
3426
  // NOT re-send this payload on its own. Daemon owns retry on
3243
3427
  // transient Lark failures.
3428
+ // A real harvested answer is a definitive self-heal signal: if the
3429
+ // session was parked in `limited` by a structured rate-limit emit
3430
+ // (Claude adopt/bridge sessions where the user recovers in their own
3431
+ // terminal — no Lark turn, retry button, or kill to clear it), the
3432
+ // model is plainly working again. Clear the stale limit so the card /
3433
+ // Dashboard「需要你」 don't stay pinned with a dead retry countdown.
3434
+ if (ds.usageLimit) {
3435
+ clearUsageLimitState(ds);
3436
+ if (ds.lastScreenStatus === 'limited')
3437
+ ds.lastScreenStatus = 'idle';
3438
+ }
3244
3439
  deliverFinalOutput(ds, msg, t, 0);
3245
3440
  break;
3246
3441
  }
@@ -3297,6 +3492,7 @@ function setupWorkerHandlers(ds, worker, startupState = { ready: false, failureN
3297
3492
  // A stale takeover worker never clears the replacement — during takeover the
3298
3493
  // old worker's exit fires AFTER the new worker has been assigned.
3299
3494
  if (ds.worker === worker) {
3495
+ restartCoordinator.failSession(ds.session.sessionId);
3300
3496
  ds.worker = null;
3301
3497
  ds.workerPort = null;
3302
3498
  ds.managedTurnOrigin = undefined;
@@ -3443,9 +3639,14 @@ function deliverFinalOutput(ds, msg, t, attempt) {
3443
3639
  }
3444
3640
  const asyncResult = managedReceiver ? undefined : ds.asyncTriggerResults?.get(msg.turnId);
3445
3641
  if (asyncResult) {
3642
+ const completedAt = Date.now();
3446
3643
  asyncResult.status = 'completed';
3447
3644
  asyncResult.content = msg.content;
3448
- asyncResult.completedAt = Date.now();
3645
+ asyncResult.completedAt = completedAt;
3646
+ // Durably persist the outcome so trigger-result can rebuild `completed`
3647
+ // (with content) after a daemon restart drops the in-memory Map. Stamp the
3648
+ // owning bot for cross-bot isolation.
3649
+ asyncTriggerStore.recordCompleted(ds.session.sessionId, msg.turnId, msg.content, completedAt, ds.larkAppId);
3449
3650
  ds.lastBridgeEmittedUuid = finalOutputDedupeKey(ds, msg);
3450
3651
  logger.info(`[${t}] Captured final_output for Async HTTP request (turn ${msg.turnId.substring(0, 8)})`);
3451
3652
  return;
@@ -3543,12 +3744,48 @@ function deliverFinalOutput(ds, msg, t, attempt) {
3543
3744
  && managedDecision.kind === 'listener_thread'
3544
3745
  ? managedDecision.meetingOwner
3545
3746
  : undefined;
3747
+ const listenerOutputPlacement = managedDecision?.ok
3748
+ && managedDecision.kind === 'listener_thread'
3749
+ ? managedDecision.outputPlacement
3750
+ : 'auto';
3751
+ const listenerOutputProtocol = managedDecision?.ok
3752
+ && managedDecision.kind === 'listener_thread'
3753
+ ? managedDecision.listenerOutputProtocol
3754
+ : 'plain';
3755
+ const meetingTopicKey = !imOrigin
3756
+ && listenerOutputOwner
3757
+ && listenerOutputPlacement === 'topic'
3758
+ ? {
3759
+ ...listenerOutputOwner,
3760
+ targetChatId: ds.chatId,
3761
+ }
3762
+ : undefined;
3763
+ let visibleAssistantText = msg.content;
3764
+ if (!imOrigin
3765
+ && listenerOutputOwner
3766
+ && msg.dispatchAttempt !== undefined
3767
+ && listenerOutputProtocol === 'decision_v1') {
3768
+ const controlledOutput = parseVcMeetingListenerOutput(msg.content);
3769
+ if (!controlledOutput.ok) {
3770
+ ds.lastBridgeEmittedUuid = finalOutputDedupeKey(ds, msg);
3771
+ logger.error(`[${t}] VC listener output suppressed: invalid control envelope `
3772
+ + `(${controlledOutput.reason}) turn=${msg.turnId.substring(0, 8)}`);
3773
+ return;
3774
+ }
3775
+ if (controlledOutput.decision === 'skip') {
3776
+ ds.lastBridgeEmittedUuid = finalOutputDedupeKey(ds, msg);
3777
+ logger.info(`[${t}] VC listener output skipped by agent decision `
3778
+ + `(turn ${msg.turnId.substring(0, 8)})`);
3779
+ return;
3780
+ }
3781
+ visibleAssistantText = controlledOutput.content;
3782
+ }
3546
3783
  // Meeting-derived text is untrusted card markdown. A model-authored
3547
3784
  // native <at> tag and the ordinary owner footer would both create a
3548
3785
  // second addressing side effect outside the action ledger.
3549
3786
  const safeAssistantText = managedReceiver
3550
- ? neutralizeLarkAtTags(msg.content)
3551
- : msg.content;
3787
+ ? neutralizeLarkAtTags(visibleAssistantText)
3788
+ : visibleAssistantText;
3552
3789
  const safeUserText = managedReceiver && msg.userText !== undefined
3553
3790
  ? neutralizeLarkAtTags(msg.userText)
3554
3791
  : msg.userText;
@@ -3575,6 +3812,9 @@ function deliverFinalOutput(ds, msg, t, attempt) {
3575
3812
  const proposedOutput = {
3576
3813
  targetChatId: ds.chatId,
3577
3814
  ...(imOrigin ? { quoteTargetId: imOrigin.larkMessageId } : {}),
3815
+ ...(!imOrigin && listenerOutputOwner
3816
+ ? { placement: listenerOutputPlacement }
3817
+ : {}),
3578
3818
  msgType: 'interactive',
3579
3819
  content: cardJson,
3580
3820
  };
@@ -3605,6 +3845,13 @@ function deliverFinalOutput(ds, msg, t, attempt) {
3605
3845
  const recordPrimaryOutput = (messageId) => {
3606
3846
  if (!listenerOutputOwner)
3607
3847
  return;
3848
+ if (meetingTopicKey
3849
+ && !getVcMeetingListenerTopicRoot(config.session.dataDir, meetingTopicKey)) {
3850
+ const topic = recordVcMeetingListenerTopicRoot(config.session.dataDir, meetingTopicKey, messageId);
3851
+ if (!topic.ok) {
3852
+ logger.error(`[${t}] VC listener topic anchor rejected message=${messageId} reason=${topic.reason}`);
3853
+ }
3854
+ }
3608
3855
  try {
3609
3856
  const recorded = recordVcMeetingListenerMessage(config.session.dataDir, {
3610
3857
  ...listenerOutputOwner,
@@ -3642,6 +3889,12 @@ function deliverFinalOutput(ds, msg, t, attempt) {
3642
3889
  // provider call). Never fan meeting content out to user hooks,
3643
3890
  // including the first attempt and crash reconciliation replay.
3644
3891
  suppressHook: true,
3892
+ ...(!imOrigin && listenerOutputOwner
3893
+ ? {
3894
+ placement: canonicalOutput.placement ?? 'auto',
3895
+ ...(meetingTopicKey ? { meetingTopicKey } : {}),
3896
+ }
3897
+ : {}),
3645
3898
  }
3646
3899
  : undefined);
3647
3900
  recordPrimaryOutput(messageId);
@@ -3701,6 +3954,30 @@ function reserveWorkerGeneration(ds) {
3701
3954
  }
3702
3955
  return workerGeneration;
3703
3956
  }
3957
+ /**
3958
+ * Is the file sandbox active such that /adopt must be refused? A sandbox can
3959
+ * only be established at spawn time (bwrap wrap / Seatbelt profile); adopt
3960
+ * attaches to an ALREADY-running host process, so it could only ever run
3961
+ * UNsandboxed. From-strict UNION of every source that can require isolation:
3962
+ * - live per-bot `sandbox` / legacy `readIsolation`,
3963
+ * - the global `BOTMUX_SANDBOX=1` (sandboxEnabled()),
3964
+ * - the session's FROZEN sandbox decision (`session.sandbox`, recorded at
3965
+ * creation). forkWorker treats the frozen decision as authoritative, so a
3966
+ * session created sandboxed must stay un-adoptable even if the admin later
3967
+ * toggles the bot's global flag OFF — otherwise its `/adopt` would attach to
3968
+ * an unsandboxed live process. The reverse (bot=true / session=false) is
3969
+ * also caught by the union, which is the safe direction.
3970
+ * Callers reject at the ENTRY point (before persisting `adoptedFrom` / replying
3971
+ * "adopted") — see command-handler's `/adopt`, the adopt_select card, and the
3972
+ * session-manager restore branch. Resume/cold-start is unaffected: those spawn
3973
+ * a fresh CLI, which the sandbox wraps normally.
3974
+ */
3975
+ export function adoptSandboxBlocked(botCfg, session) {
3976
+ return botCfg.sandbox === true
3977
+ || botCfg.readIsolation === true
3978
+ || session?.sandbox === true
3979
+ || sandboxEnabled();
3980
+ }
3704
3981
  export function forkAdoptWorker(ds, opts) {
3705
3982
  const cb = requireCallbacks();
3706
3983
  const workerPath = join(__dirname, '..', 'worker.js');
@@ -3710,12 +3987,30 @@ export function forkAdoptWorker(ds, opts) {
3710
3987
  throw new Error('forkAdoptWorker called without adoptedFrom');
3711
3988
  const bot = getBot(ds.larkAppId);
3712
3989
  const botCfg = bot.config;
3713
- // Read isolation cannot be applied to an already-running CLI (adopt attaches
3714
- // to an existing pane; we can't inject --settings into it). Refuse to adopt an
3715
- // isolated bot rather than run it unisolated it will cold-start (isolated)
3716
- // via forkWorker on the next message instead. (Codex review #2, fail-closed.)
3717
- if (botCfg.readIsolation === true) {
3718
- logger.warn(`[${t}] read-isolation bot: refusing to adopt existing CLI (would run unisolated); will cold-start isolated on next message`);
3990
+ // A file sandbox cannot be applied to an already-running CLI: adopt ATTACHES
3991
+ // to an existing host pane/process, and confinement (bwrap wrap on Linux /
3992
+ // Seatbelt profile on macOS) can only be established at spawn time — there is
3993
+ // no way to retro-fit a sandbox around a live process. Refuse to adopt when
3994
+ // the sandbox is active (live bot flag OR the session's FROZEN decision)
3995
+ // rather than run it UNsandboxed. Real adopt entry points (`/adopt`, the
3996
+ // adopt_select card) reject BEFORE persisting `adoptedFrom`, and the
3997
+ // session-manager restore branch converts a sandbox adopt to a cold-start
3998
+ // before it ever reaches here; this is the last-line fail-closed backstop,
3999
+ // which also strips any stale adopt metadata it is handed.
4000
+ if (adoptSandboxBlocked(botCfg, ds.session)) {
4001
+ logger.warn(`[${t}] sandbox-enabled session: refusing to adopt existing CLI (would run unsandboxed)`);
4002
+ // Strip stale adopt state so the session doesn't linger as a worker=null
4003
+ // pseudo-adopt whose NEXT message
4004
+ // would still be routed as a bridge/adopt session. It cold-starts sandboxed
4005
+ // via the normal resume path instead.
4006
+ ds.adoptedFrom = undefined;
4007
+ if (ds.session.adoptedFrom) {
4008
+ ds.session.adoptedFrom = undefined;
4009
+ try {
4010
+ sessionStore.updateSession(ds.session);
4011
+ }
4012
+ catch { /* best-effort */ }
4013
+ }
3719
4014
  return;
3720
4015
  }
3721
4016
  // Reserve before replacing an existing bridge worker for the same reason as