botmux 3.9.2 → 3.10.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 (314) hide show
  1. package/README.en.md +1 -1
  2. package/README.md +1 -1
  3. package/dist/.runtime-build-id +1 -1
  4. package/dist/adapters/cli/codex.d.ts.map +1 -1
  5. package/dist/adapters/cli/codex.js +6 -0
  6. package/dist/adapters/cli/codex.js.map +1 -1
  7. package/dist/adapters/cli/opencode.d.ts.map +1 -1
  8. package/dist/adapters/cli/opencode.js +7 -1
  9. package/dist/adapters/cli/opencode.js.map +1 -1
  10. package/dist/adapters/cli/reasonix.d.ts +35 -0
  11. package/dist/adapters/cli/reasonix.d.ts.map +1 -0
  12. package/dist/adapters/cli/reasonix.js +236 -0
  13. package/dist/adapters/cli/reasonix.js.map +1 -0
  14. package/dist/adapters/cli/registry.d.ts +2 -1
  15. package/dist/adapters/cli/registry.d.ts.map +1 -1
  16. package/dist/adapters/cli/registry.js +4 -1
  17. package/dist/adapters/cli/registry.js.map +1 -1
  18. package/dist/adapters/cli/types.d.ts +5 -1
  19. package/dist/adapters/cli/types.d.ts.map +1 -1
  20. package/dist/cli.d.ts.map +1 -1
  21. package/dist/cli.js +168 -62
  22. package/dist/cli.js.map +1 -1
  23. package/dist/core/command-handler.d.ts +18 -4
  24. package/dist/core/command-handler.d.ts.map +1 -1
  25. package/dist/core/command-handler.js +234 -26
  26. package/dist/core/command-handler.js.map +1 -1
  27. package/dist/core/dashboard-ipc-server.d.ts +1 -0
  28. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  29. package/dist/core/dashboard-ipc-server.js +27 -3
  30. package/dist/core/dashboard-ipc-server.js.map +1 -1
  31. package/dist/core/dispatch.d.ts +54 -24
  32. package/dist/core/dispatch.d.ts.map +1 -1
  33. package/dist/core/dispatch.js +81 -27
  34. package/dist/core/dispatch.js.map +1 -1
  35. package/dist/core/input-turn-dedupe.d.ts +20 -0
  36. package/dist/core/input-turn-dedupe.d.ts.map +1 -0
  37. package/dist/core/input-turn-dedupe.js +57 -0
  38. package/dist/core/input-turn-dedupe.js.map +1 -0
  39. package/dist/core/local-terminal-opener.d.ts.map +1 -1
  40. package/dist/core/local-terminal-opener.js +10 -1
  41. package/dist/core/local-terminal-opener.js.map +1 -1
  42. package/dist/core/passthrough-commands.d.ts.map +1 -1
  43. package/dist/core/passthrough-commands.js +1 -1
  44. package/dist/core/passthrough-commands.js.map +1 -1
  45. package/dist/core/plugins/pm2.d.ts.map +1 -1
  46. package/dist/core/plugins/pm2.js +7 -1
  47. package/dist/core/plugins/pm2.js.map +1 -1
  48. package/dist/core/resource-monitor/attribution.d.ts.map +1 -1
  49. package/dist/core/resource-monitor/attribution.js +17 -1
  50. package/dist/core/resource-monitor/attribution.js.map +1 -1
  51. package/dist/core/resource-monitor/darwin.d.ts +145 -0
  52. package/dist/core/resource-monitor/darwin.d.ts.map +1 -0
  53. package/dist/core/resource-monitor/darwin.js +360 -0
  54. package/dist/core/resource-monitor/darwin.js.map +1 -0
  55. package/dist/core/resource-monitor/procfs.d.ts.map +1 -1
  56. package/dist/core/resource-monitor/procfs.js +6 -0
  57. package/dist/core/resource-monitor/procfs.js.map +1 -1
  58. package/dist/core/resource-monitor/types.d.ts +1 -1
  59. package/dist/core/resource-monitor/types.d.ts.map +1 -1
  60. package/dist/core/scheduler.d.ts.map +1 -1
  61. package/dist/core/scheduler.js +5 -2
  62. package/dist/core/scheduler.js.map +1 -1
  63. package/dist/core/session-discovery.d.ts.map +1 -1
  64. package/dist/core/session-discovery.js +3 -0
  65. package/dist/core/session-discovery.js.map +1 -1
  66. package/dist/core/session-manager.d.ts +3 -1
  67. package/dist/core/session-manager.d.ts.map +1 -1
  68. package/dist/core/session-manager.js +46 -0
  69. package/dist/core/session-manager.js.map +1 -1
  70. package/dist/core/session-marker.d.ts +1 -8
  71. package/dist/core/session-marker.d.ts.map +1 -1
  72. package/dist/core/session-marker.js +2 -50
  73. package/dist/core/session-marker.js.map +1 -1
  74. package/dist/core/types.d.ts +3 -1
  75. package/dist/core/types.d.ts.map +1 -1
  76. package/dist/core/types.js.map +1 -1
  77. package/dist/core/worker-pool.d.ts +17 -0
  78. package/dist/core/worker-pool.d.ts.map +1 -1
  79. package/dist/core/worker-pool.js +292 -9
  80. package/dist/core/worker-pool.js.map +1 -1
  81. package/dist/daemon.d.ts.map +1 -1
  82. package/dist/daemon.js +27 -12
  83. package/dist/daemon.js.map +1 -1
  84. package/dist/dashboard/daemon-internal-api.d.ts.map +1 -1
  85. package/dist/dashboard/daemon-internal-api.js +11 -2
  86. package/dist/dashboard/daemon-internal-api.js.map +1 -1
  87. package/dist/dashboard/groups-action-helpers.d.ts +2 -0
  88. package/dist/dashboard/groups-action-helpers.d.ts.map +1 -1
  89. package/dist/dashboard/groups-action-helpers.js +9 -0
  90. package/dist/dashboard/groups-action-helpers.js.map +1 -1
  91. package/dist/dashboard/groups-matrix-snapshot.d.ts +56 -0
  92. package/dist/dashboard/groups-matrix-snapshot.d.ts.map +1 -0
  93. package/dist/dashboard/groups-matrix-snapshot.js +127 -0
  94. package/dist/dashboard/groups-matrix-snapshot.js.map +1 -0
  95. package/dist/dashboard/resource-monitor-service.d.ts.map +1 -1
  96. package/dist/dashboard/resource-monitor-service.js +18 -2
  97. package/dist/dashboard/resource-monitor-service.js.map +1 -1
  98. package/dist/dashboard/web/app.d.ts.map +1 -1
  99. package/dist/dashboard/web/app.js +45 -4
  100. package/dist/dashboard/web/app.js.map +1 -1
  101. package/dist/dashboard/web/groups-api.js +1 -1
  102. package/dist/dashboard/web/groups-api.js.map +1 -1
  103. package/dist/dashboard/web/i18n.js +10 -10
  104. package/dist/dashboard/web/i18n.js.map +1 -1
  105. package/dist/dashboard/web/roles-page.d.ts.map +1 -1
  106. package/dist/dashboard/web/roles-page.js +26 -11
  107. package/dist/dashboard/web/roles-page.js.map +1 -1
  108. package/dist/dashboard-web/app.js +1 -1
  109. package/dist/dashboard-web/chunks/{bot-defaults-page-BHTQEWCH.js → bot-defaults-page-LTVOUDIS.js} +1 -1
  110. package/dist/dashboard-web/chunks/{chunk-KLZCQAHF.js → chunk-3UE7S77S.js} +1 -1
  111. package/dist/dashboard-web/chunks/{chunk-W77TPKZH.js → chunk-DCS35UMD.js} +1 -1
  112. package/dist/dashboard-web/chunks/{chunk-E3HZZXK5.js → chunk-MFQPCVHY.js} +4 -4
  113. package/dist/dashboard-web/chunks/{chunk-EZH56JCI.js → chunk-ZU3UUKAR.js} +1 -1
  114. package/dist/dashboard-web/chunks/{connectors-page-BP255CAX.js → connectors-page-5KWDIMKC.js} +1 -1
  115. package/dist/dashboard-web/chunks/{groups-page-CDHVXBUT.js → groups-page-FHOKYXMW.js} +1 -1
  116. package/dist/dashboard-web/chunks/{insights-page-57BFE6GT.js → insights-page-T2ZX2RWL.js} +1 -1
  117. package/dist/dashboard-web/chunks/{monitor-room-UTOYGQVF.js → monitor-room-YNVBAGR6.js} +1 -1
  118. package/dist/dashboard-web/chunks/{monitoring-page-WTHYAIUM.js → monitoring-page-LHM55YWN.js} +1 -1
  119. package/dist/dashboard-web/chunks/{office-page-JSMIB353.js → office-page-ATXVP7PW.js} +1 -1
  120. package/dist/dashboard-web/chunks/{overview-page-3VDBOH7L.js → overview-page-QYI3KBDD.js} +1 -1
  121. package/dist/dashboard-web/chunks/roles-page-56VWM6CD.js +1 -0
  122. package/dist/dashboard-web/chunks/{schedules-page-HHN736H6.js → schedules-page-S5K6U4EO.js} +1 -1
  123. package/dist/dashboard-web/chunks/{sessions-page-EIWOZD7A.js → sessions-page-WBP4N7D2.js} +1 -1
  124. package/dist/dashboard-web/chunks/{settings-page-2IUT5LS4.js → settings-page-QUZDQARR.js} +1 -1
  125. package/dist/dashboard-web/chunks/{skills-page-JKFKMUEM.js → skills-page-IFY7AEND.js} +1 -1
  126. package/dist/dashboard-web/chunks/{team-federation-page-Q2PXC3UY.js → team-federation-page-C7FJQPFZ.js} +1 -1
  127. package/dist/dashboard-web/chunks/{v3-page-EBQCD4AO.js → v3-page-2LMF3WWR.js} +1 -1
  128. package/dist/dashboard-web/chunks/{whiteboards-page-QZRMVNZN.js → whiteboards-page-3XC6QRMY.js} +1 -1
  129. package/dist/dashboard-web/style.css +2 -4
  130. package/dist/dashboard.js +65 -9
  131. package/dist/dashboard.js.map +1 -1
  132. package/dist/i18n/en.d.ts.map +1 -1
  133. package/dist/i18n/en.js +33 -7
  134. package/dist/i18n/en.js.map +1 -1
  135. package/dist/i18n/zh.d.ts.map +1 -1
  136. package/dist/i18n/zh.js +31 -7
  137. package/dist/i18n/zh.js.map +1 -1
  138. package/dist/im/lark/card-builder.d.ts +7 -0
  139. package/dist/im/lark/card-builder.d.ts.map +1 -1
  140. package/dist/im/lark/card-builder.js +56 -0
  141. package/dist/im/lark/card-builder.js.map +1 -1
  142. package/dist/im/lark/card-handler.d.ts.map +1 -1
  143. package/dist/im/lark/card-handler.js +42 -19
  144. package/dist/im/lark/card-handler.js.map +1 -1
  145. package/dist/im/lark/client.d.ts +10 -0
  146. package/dist/im/lark/client.d.ts.map +1 -1
  147. package/dist/im/lark/client.js +79 -0
  148. package/dist/im/lark/client.js.map +1 -1
  149. package/dist/im/lark/event-dispatcher.d.ts +14 -1
  150. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  151. package/dist/im/lark/event-dispatcher.js +171 -15
  152. package/dist/im/lark/event-dispatcher.js.map +1 -1
  153. package/dist/im/lark/lark-hosts.d.ts +5 -0
  154. package/dist/im/lark/lark-hosts.d.ts.map +1 -1
  155. package/dist/im/lark/lark-hosts.js +10 -0
  156. package/dist/im/lark/lark-hosts.js.map +1 -1
  157. package/dist/im/lark/message-parser.d.ts.map +1 -1
  158. package/dist/im/lark/message-parser.js +24 -7
  159. package/dist/im/lark/message-parser.js.map +1 -1
  160. package/dist/pm2-graceful-exit.d.ts +28 -0
  161. package/dist/pm2-graceful-exit.d.ts.map +1 -0
  162. package/dist/pm2-graceful-exit.js +39 -0
  163. package/dist/pm2-graceful-exit.js.map +1 -0
  164. package/dist/services/bridge-fallback-gate.d.ts +9 -2
  165. package/dist/services/bridge-fallback-gate.d.ts.map +1 -1
  166. package/dist/services/bridge-fallback-gate.js +31 -19
  167. package/dist/services/bridge-fallback-gate.js.map +1 -1
  168. package/dist/services/message-listener-store.d.ts +18 -0
  169. package/dist/services/message-listener-store.d.ts.map +1 -1
  170. package/dist/services/message-listener-store.js +35 -10
  171. package/dist/services/message-listener-store.js.map +1 -1
  172. package/dist/services/message-listener.d.ts +17 -0
  173. package/dist/services/message-listener.d.ts.map +1 -1
  174. package/dist/services/message-listener.js +26 -0
  175. package/dist/services/message-listener.js.map +1 -1
  176. package/dist/setup/bot-config-editor.d.ts.map +1 -1
  177. package/dist/setup/bot-config-editor.js +2 -0
  178. package/dist/setup/bot-config-editor.js.map +1 -1
  179. package/dist/setup/lark-scopes.json +1 -0
  180. package/dist/setup/verify-permissions.d.ts.map +1 -1
  181. package/dist/setup/verify-permissions.js +8 -0
  182. package/dist/setup/verify-permissions.js.map +1 -1
  183. package/dist/types.d.ts +34 -0
  184. package/dist/types.d.ts.map +1 -1
  185. package/dist/utils/child-env.d.ts +1 -1
  186. package/dist/utils/child-env.d.ts.map +1 -1
  187. package/dist/utils/child-env.js +13 -0
  188. package/dist/utils/child-env.js.map +1 -1
  189. package/dist/utils/file-lock.d.ts.map +1 -1
  190. package/dist/utils/file-lock.js +1 -1
  191. package/dist/utils/file-lock.js.map +1 -1
  192. package/dist/utils/global-install.d.ts.map +1 -1
  193. package/dist/utils/global-install.js +56 -10
  194. package/dist/utils/global-install.js.map +1 -1
  195. package/dist/utils/idle-detector.d.ts +4 -0
  196. package/dist/utils/idle-detector.d.ts.map +1 -1
  197. package/dist/utils/idle-detector.js +25 -0
  198. package/dist/utils/idle-detector.js.map +1 -1
  199. package/dist/utils/process-identity.d.ts +7 -0
  200. package/dist/utils/process-identity.d.ts.map +1 -0
  201. package/dist/utils/process-identity.js +62 -0
  202. package/dist/utils/process-identity.js.map +1 -0
  203. package/dist/worker.js +123 -9
  204. package/dist/worker.js.map +1 -1
  205. package/dist/workflows/events/payloads.d.ts +10 -10
  206. package/dist/workflows/events/schema.d.ts +28 -28
  207. package/dist/workflows/hostExecutors/registry.d.ts +2 -7
  208. package/dist/workflows/hostExecutors/registry.d.ts.map +1 -1
  209. package/dist/workflows/hostExecutors/registry.js.map +1 -1
  210. package/dist/workflows/shared/provider-reconciler.d.ts +1 -30
  211. package/dist/workflows/shared/provider-reconciler.d.ts.map +1 -1
  212. package/dist/workflows/shared/provider-reconciler.js.map +1 -1
  213. package/dist/workflows/v3/artifact-contract.d.ts +41 -0
  214. package/dist/workflows/v3/artifact-contract.d.ts.map +1 -0
  215. package/dist/workflows/v3/artifact-contract.js +20 -0
  216. package/dist/workflows/v3/artifact-contract.js.map +1 -0
  217. package/dist/workflows/v3/botmux-attempt-lease.d.ts +7 -0
  218. package/dist/workflows/v3/botmux-attempt-lease.d.ts.map +1 -0
  219. package/dist/workflows/v3/botmux-attempt-lease.js +189 -0
  220. package/dist/workflows/v3/botmux-attempt-lease.js.map +1 -0
  221. package/dist/workflows/v3/botmux-host-policy.d.ts +7 -0
  222. package/dist/workflows/v3/botmux-host-policy.d.ts.map +1 -0
  223. package/dist/workflows/v3/botmux-host-policy.js +34 -0
  224. package/dist/workflows/v3/botmux-host-policy.js.map +1 -0
  225. package/dist/workflows/v3/cli-run.d.ts +1 -1
  226. package/dist/workflows/v3/cli-run.d.ts.map +1 -1
  227. package/dist/workflows/v3/cli-run.js +1 -1
  228. package/dist/workflows/v3/cli-run.js.map +1 -1
  229. package/dist/workflows/v3/contract.d.ts +8 -89
  230. package/dist/workflows/v3/contract.d.ts.map +1 -1
  231. package/dist/workflows/v3/contract.js +2 -8
  232. package/dist/workflows/v3/contract.js.map +1 -1
  233. package/dist/workflows/v3/core-control.d.ts +16 -0
  234. package/dist/workflows/v3/core-control.d.ts.map +1 -0
  235. package/dist/workflows/v3/core-control.js +60 -0
  236. package/dist/workflows/v3/core-control.js.map +1 -0
  237. package/dist/workflows/v3/daemon-run.d.ts.map +1 -1
  238. package/dist/workflows/v3/daemon-run.js +2 -1
  239. package/dist/workflows/v3/daemon-run.js.map +1 -1
  240. package/dist/workflows/v3/dag-loader.d.ts +5 -0
  241. package/dist/workflows/v3/dag-loader.d.ts.map +1 -0
  242. package/dist/workflows/v3/dag-loader.js +22 -0
  243. package/dist/workflows/v3/dag-loader.js.map +1 -0
  244. package/dist/workflows/v3/dag.d.ts +2 -6
  245. package/dist/workflows/v3/dag.d.ts.map +1 -1
  246. package/dist/workflows/v3/dag.js +2 -24
  247. package/dist/workflows/v3/dag.js.map +1 -1
  248. package/dist/workflows/v3/ephemeral-pool.d.ts.map +1 -1
  249. package/dist/workflows/v3/ephemeral-pool.js +22 -12
  250. package/dist/workflows/v3/ephemeral-pool.js.map +1 -1
  251. package/dist/workflows/v3/event-contract.d.ts +273 -0
  252. package/dist/workflows/v3/event-contract.d.ts.map +1 -0
  253. package/dist/workflows/v3/event-contract.js +8 -0
  254. package/dist/workflows/v3/event-contract.js.map +1 -0
  255. package/dist/workflows/v3/gate-policy.d.ts +12 -0
  256. package/dist/workflows/v3/gate-policy.d.ts.map +1 -0
  257. package/dist/workflows/v3/gate-policy.js +20 -0
  258. package/dist/workflows/v3/gate-policy.js.map +1 -0
  259. package/dist/workflows/v3/gate-wait-store.d.ts +99 -0
  260. package/dist/workflows/v3/gate-wait-store.d.ts.map +1 -0
  261. package/dist/workflows/v3/gate-wait-store.js +263 -0
  262. package/dist/workflows/v3/gate-wait-store.js.map +1 -0
  263. package/dist/workflows/v3/host.js +1 -1
  264. package/dist/workflows/v3/host.js.map +1 -1
  265. package/dist/workflows/v3/human-gate.d.ts +4 -99
  266. package/dist/workflows/v3/human-gate.d.ts.map +1 -1
  267. package/dist/workflows/v3/human-gate.js +4 -275
  268. package/dist/workflows/v3/human-gate.js.map +1 -1
  269. package/dist/workflows/v3/in-process-attempt-lease.d.ts +10 -0
  270. package/dist/workflows/v3/in-process-attempt-lease.d.ts.map +1 -0
  271. package/dist/workflows/v3/in-process-attempt-lease.js +47 -0
  272. package/dist/workflows/v3/in-process-attempt-lease.js.map +1 -0
  273. package/dist/workflows/v3/journal.d.ts +2 -275
  274. package/dist/workflows/v3/journal.d.ts.map +1 -1
  275. package/dist/workflows/v3/journal.js.map +1 -1
  276. package/dist/workflows/v3/library-service.js +1 -1
  277. package/dist/workflows/v3/library-service.js.map +1 -1
  278. package/dist/workflows/v3/orchestrator.d.ts +1 -2
  279. package/dist/workflows/v3/orchestrator.d.ts.map +1 -1
  280. package/dist/workflows/v3/orchestrator.js.map +1 -1
  281. package/dist/workflows/v3/portable-artifact-snapshot.d.ts +15 -0
  282. package/dist/workflows/v3/portable-artifact-snapshot.d.ts.map +1 -0
  283. package/dist/workflows/v3/portable-artifact-snapshot.js +76 -0
  284. package/dist/workflows/v3/portable-artifact-snapshot.js.map +1 -0
  285. package/dist/workflows/v3/portable-final-outputs.d.ts +37 -0
  286. package/dist/workflows/v3/portable-final-outputs.d.ts.map +1 -0
  287. package/dist/workflows/v3/portable-final-outputs.js +137 -0
  288. package/dist/workflows/v3/portable-final-outputs.js.map +1 -0
  289. package/dist/workflows/v3/portable-run-snapshot.d.ts +14 -0
  290. package/dist/workflows/v3/portable-run-snapshot.d.ts.map +1 -0
  291. package/dist/workflows/v3/portable-run-snapshot.js +176 -0
  292. package/dist/workflows/v3/portable-run-snapshot.js.map +1 -0
  293. package/dist/workflows/v3/portable-runtime.d.ts +83 -0
  294. package/dist/workflows/v3/portable-runtime.d.ts.map +1 -0
  295. package/dist/workflows/v3/portable-runtime.js +135 -0
  296. package/dist/workflows/v3/portable-runtime.js.map +1 -0
  297. package/dist/workflows/v3/runtime-host-contract.d.ts +182 -0
  298. package/dist/workflows/v3/runtime-host-contract.d.ts.map +1 -0
  299. package/dist/workflows/v3/runtime-host-contract.js +9 -0
  300. package/dist/workflows/v3/runtime-host-contract.js.map +1 -0
  301. package/dist/workflows/v3/runtime.d.ts +12 -226
  302. package/dist/workflows/v3/runtime.d.ts.map +1 -1
  303. package/dist/workflows/v3/runtime.js +14 -3173
  304. package/dist/workflows/v3/runtime.js.map +1 -1
  305. package/dist/workflows/v3/shared-node-runtime.d.ts +203 -0
  306. package/dist/workflows/v3/shared-node-runtime.d.ts.map +1 -0
  307. package/dist/workflows/v3/shared-node-runtime.js +2978 -0
  308. package/dist/workflows/v3/shared-node-runtime.js.map +1 -0
  309. package/dist/workflows/v3/shared-runtime.d.ts +17 -0
  310. package/dist/workflows/v3/shared-runtime.d.ts.map +1 -0
  311. package/dist/workflows/v3/shared-runtime.js +12 -0
  312. package/dist/workflows/v3/shared-runtime.js.map +1 -0
  313. package/package.json +4 -1
  314. package/dist/dashboard-web/chunks/roles-page-LHVAU2BI.js +0 -1
@@ -24,12 +24,11 @@ export declare const SESSIONLESS_DAEMON_COMMANDS: Set<string>;
24
24
  export declare function formatSlashGroupName(name: string, prefix?: string): string;
25
25
  /**
26
26
  * Daemon commands that operate on an ALREADY-EXISTING session and must never
27
- * pre-create one. `/rename` renames the current session with no session there
28
- * is nothing to rename, so the daemon routes must skip their generic
27
+ * pre-create one. With no real session to operate on, the daemon routes must skip their generic
29
28
  * "createSession + activeSessions.set(worker:null)" pre-create block and let
30
- * handleCommand's `!ds` branch reply no_active_session. Without this, `/rename`
29
+ * handleCommand's `!ds` branch reply no_active_session. Without this, one of these commands
31
30
  * in a brand-new topic (or a thread with no session) would spawn a phantom
32
- * worker:null session just to rename it, polluting the dashboard. (Same class
31
+ * worker:null session just to handle it, polluting the dashboard. (Same class
33
32
  * of fix as the `/card` / `/term` special cases in daemon.ts.)
34
33
  */
35
34
  export declare const EXISTING_SESSION_ONLY_DAEMON_COMMANDS: Set<string>;
@@ -146,4 +145,19 @@ export declare function discoverResumableSessionsForBot(cliId: CliId, cliPathOve
146
145
  * CLI via `--resume <cliSessionId>` in `cwd`. Mirrors the manual resume path —
147
146
  * the worker owns the CLI (NOT an observe-adopt), so no `adoptedFrom` is set. */
148
147
  export declare function startResumeImportSession(target: ResumableSession, ds: DaemonSession, deps: CommandHandlerDeps, larkAppId?: string): Promise<void>;
148
+ type ForkSubtopicResult = {
149
+ ok: true;
150
+ childSessionId: string;
151
+ anchorId: string;
152
+ link: string;
153
+ } | {
154
+ ok: false;
155
+ error: string;
156
+ orphanTopic: boolean;
157
+ };
158
+ /** Fork the current session into a new sub-topic of the same topic group.
159
+ * The session copy itself stays in worker-pool's generic `forkSession()`;
160
+ * this layer only creates the Lark destination, supplies the first task turn,
161
+ * and records display-only lineage for the parent panel. */
162
+ export declare function startForkSubtopicSession(taskText: string, parentDs: DaemonSession, message: LarkMessage, larkAppId?: string): Promise<ForkSubtopicResult>;
149
163
  //# sourceMappingURL=command-handler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"command-handler.d.ts","sourceRoot":"","sources":["../../src/core/command-handler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"command-handler.d.ts","sourceRoot":"","sources":["../../src/core/command-handler.ts"],"names":[],"mappings":"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"}
@@ -15,12 +15,12 @@ import { scanMultipleProjects, describeProjectDir } from '../services/project-sc
15
15
  import { createRepoWorktree, pushWorktreeBranch } from '../services/git-worktree.js';
16
16
  import { worktreeSlugFromContextAI } from '../services/worktree-slug-ai.js';
17
17
  import { resolvePairedSpawnBackendType } from './persistent-backend.js';
18
- import { buildRepoSelectCard, buildAdoptSelectCard, buildCodexAppThreadSelectCard, buildSlashListCard, getCliDisplayName, buildConfigCard, buildAdoptBlockedCard } from '../im/lark/card-builder.js';
18
+ import { buildRepoSelectCard, buildAdoptSelectCard, buildCodexAppThreadSelectCard, buildSlashListCard, getCliDisplayName, buildConfigCard, buildForkPanelCard, buildAdoptBlockedCard } from '../im/lark/card-builder.js';
19
19
  import { handleDashboardCommand } from './dashboard-command/index.js';
20
20
  import { createCliAdapterSync } from '../adapters/cli/registry.js';
21
21
  import { resolveCliRuntime, runtimeInstallationKey } from '../adapters/cli/runtime.js';
22
- import { deleteMessage, sendMessage, sendUserMessage, replyMessage, listChatBotMembers, resolveUserUnionId, getChatModeStrict, UserTokenMissingError } from '../im/lark/client.js';
23
- import { chatAppLink, normalizeBrand } from '../im/lark/lark-hosts.js';
22
+ import { deleteMessage, sendMessage, sendUserMessage, replyMessage, listChatBotMembers, resolveUserUnionId, getChatModeStrict, getMessageThreadId, UserTokenMissingError } from '../im/lark/client.js';
23
+ import { chatAppLink, threadAppLink, normalizeBrand } from '../im/lark/lark-hosts.js';
24
24
  import { claimPairing } from '../services/pairing-store.js';
25
25
  import { logger } from '../utils/logger.js';
26
26
  import { scheduleTimeZone } from '../utils/timezone.js';
@@ -96,15 +96,14 @@ export function formatSlashGroupName(name, prefix = '') {
96
96
  }
97
97
  /**
98
98
  * Daemon commands that operate on an ALREADY-EXISTING session and must never
99
- * pre-create one. `/rename` renames the current session with no session there
100
- * is nothing to rename, so the daemon routes must skip their generic
99
+ * pre-create one. With no real session to operate on, the daemon routes must skip their generic
101
100
  * "createSession + activeSessions.set(worker:null)" pre-create block and let
102
- * handleCommand's `!ds` branch reply no_active_session. Without this, `/rename`
101
+ * handleCommand's `!ds` branch reply no_active_session. Without this, one of these commands
103
102
  * in a brand-new topic (or a thread with no session) would spawn a phantom
104
- * worker:null session just to rename it, polluting the dashboard. (Same class
103
+ * worker:null session just to handle it, polluting the dashboard. (Same class
105
104
  * of fix as the `/card` / `/term` special cases in daemon.ts.)
106
105
  */
107
- export const EXISTING_SESSION_ONLY_DAEMON_COMMANDS = new Set(['/rename']);
106
+ export const EXISTING_SESSION_ONLY_DAEMON_COMMANDS = new Set(['/rename', '/fork', '/forklist']);
108
107
  export function resolveAdapterDefaultPassthroughCommands(larkAppId) {
109
108
  if (!larkAppId)
110
109
  return [];
@@ -148,7 +147,7 @@ export function resolvePassthroughCommands(larkAppId) {
148
147
  }
149
148
  return effective;
150
149
  }
151
- const MULTILINE_COMMANDS = new Set(['/schedule', '/role']);
150
+ const MULTILINE_COMMANDS = new Set(['/schedule', '/role', '/fork']);
152
151
  // `validateWorkingDir` now lives in ./working-dir.js (leaf module the CLI can
153
152
  // import without the daemon graph); re-exported here for existing callers.
154
153
  export { validateWorkingDir };
@@ -1409,7 +1408,8 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
1409
1408
  // shared in-flight claim instead of treating this as a repo switch.
1410
1409
  const initiallyBuffered = (ds.pendingPrompt?.trim().length ?? 0) > 0 ||
1411
1410
  (ds.pendingAttachments?.length ?? 0) > 0 ||
1412
- (ds.pendingFollowUps?.length ?? 0) > 0;
1411
+ (ds.pendingFollowUps?.length ?? 0) > 0 ||
1412
+ ds.pendingChatContext !== undefined;
1413
1413
  const availableBots = initiallyBuffered
1414
1414
  ? await getAvailableBots(ds.larkAppId, ds.chatId)
1415
1415
  : [];
@@ -1423,7 +1423,8 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
1423
1423
  const pendingRawInput = ds.pendingRawInput;
1424
1424
  const hasBufferedInput = pendingPrompt.trim().length > 0 ||
1425
1425
  (ds.pendingAttachments?.length ?? 0) > 0 ||
1426
- (ds.pendingFollowUps?.length ?? 0) > 0;
1426
+ (ds.pendingFollowUps?.length ?? 0) > 0 ||
1427
+ ds.pendingChatContext !== undefined;
1427
1428
  // Nothing to submit at all (bare `/repo`: the message IS the command).
1428
1429
  // The CLI boots idle, so the user's NEXT real message is its first
1429
1430
  // turn and must carry the full new-topic opening — see
@@ -1442,6 +1443,7 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
1442
1443
  codexAppMessageContext: ds.pendingCodexAppMessageContext,
1443
1444
  codexAppFollowUps: ds.pendingCodexAppFollowUps,
1444
1445
  codexAppFollowUpContexts: ds.pendingCodexAppFollowUpContexts,
1446
+ chatContext: ds.pendingChatContext,
1445
1447
  })
1446
1448
  : { content: '' };
1447
1449
  const pendingTurnId = ds.pendingTurnId;
@@ -1490,6 +1492,7 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
1490
1492
  ds.pendingCodexAppText = undefined;
1491
1493
  ds.pendingCodexAppApplicationContext = undefined;
1492
1494
  ds.pendingCodexAppMessageContext = undefined;
1495
+ ds.pendingChatContext = undefined;
1493
1496
  ds.pendingAttachments = undefined;
1494
1497
  ds.pendingMentions = undefined;
1495
1498
  ds.pendingSubstituteTrigger = undefined;
@@ -3307,8 +3310,8 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
3307
3310
  case '/fork': {
3308
3311
  // Session fork (Bot 分身): non-destructive copy of a running session
3309
3312
  // into a SECOND independent session at a new anchor; source untouched.
3310
- // PR1 scope: `--create <群名> @bot` (fork to a freshly-created group).
3311
- // The no-arg picker (fork into the current group) is a follow-up.
3313
+ // `/fork <task>` hosts it in a new sub-topic of the same topic group;
3314
+ // `/fork --create <name>` keeps the existing new-group destination.
3312
3315
  const argsLine = message.content.replace(/^\/fork\s*/i, '').trim();
3313
3316
  const forkAppId = larkAppId ?? ds?.larkAppId;
3314
3317
  if (!forkAppId) {
@@ -3338,20 +3341,8 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
3338
3341
  await sessionReply(rootId, t('cmd.fork.unsupported_backend', { cli: cliName }, loc));
3339
3342
  break;
3340
3343
  }
3341
- if (!/^--create\b/i.test(argsLine)) {
3342
- // No-arg picker path ("fork into an existing group you pick") is not
3343
- // built yet. Don't reuse the no-session copy — the user often DOES
3344
- // have a session here (that's exactly the confusing case). Tell them
3345
- // the picker is pending and point at the working --create form.
3346
- await sessionReply(rootId, t('cmd.fork.picker_pending', undefined, loc));
3347
- break;
3348
- }
3349
- // ── /fork --create <群名> @bot ──────────────────────────────────────
3350
- const afterFlag = argsLine.replace(/^--create\s*/i, '').trim();
3351
3344
  // Front guards (fork needs a clean, real, idle source — same as relay).
3352
- // These MUST run before createGroupWithBots, otherwise a refusal (e.g.
3353
- // /fork typed at a chat top-level where ds is an empty scratch, while
3354
- // the real session lives in a 话题) leaves an orphan empty group.
3345
+ // These MUST run before creating either a topic root or a new group.
3355
3346
  if (ds.session.adoptedFrom) {
3356
3347
  await sessionReply(rootId, t('cmd.fork.adopt_not_forkable', undefined, loc));
3357
3348
  break;
@@ -3375,6 +3366,55 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
3375
3366
  await sessionReply(rootId, t('cmd.fork.mid_turn', undefined, loc));
3376
3367
  break;
3377
3368
  }
3369
+ if (!/^--create\b/i.test(argsLine)) {
3370
+ if (!argsLine) {
3371
+ await sessionReply(rootId, t('cmd.fork.subtopic_usage', undefined, loc));
3372
+ break;
3373
+ }
3374
+ if (ds.scope !== 'thread' || !ds.session.rootMessageId?.startsWith('om_')) {
3375
+ await sessionReply(rootId, t('cmd.fork.subtopic_thread_only', undefined, loc));
3376
+ break;
3377
+ }
3378
+ let chatMode;
3379
+ try {
3380
+ chatMode = await getChatModeStrict(forkAppId, ds.chatId);
3381
+ }
3382
+ catch {
3383
+ // Treat an unknown mode as unsupported: sending a top-level message
3384
+ // to a regular group would not create the isolated topic we promise.
3385
+ }
3386
+ if (chatMode !== 'topic') {
3387
+ await sessionReply(rootId, t('cmd.fork.subtopic_thread_only', undefined, loc));
3388
+ break;
3389
+ }
3390
+ const result = await startForkSubtopicSession(argsLine, ds, message, forkAppId);
3391
+ if (!result.ok) {
3392
+ const errKey = result.error === 'worker_busy' ? 'cmd.fork.mid_turn'
3393
+ : result.error === 'adopt_not_forkable' ? 'cmd.fork.adopt_not_forkable'
3394
+ : result.error === 'fork_unsupported_backend' ? 'cmd.fork.unsupported_backend'
3395
+ : result.error === 'not_started_yet' ? 'cmd.fork.not_started_yet'
3396
+ : undefined;
3397
+ if (errKey === 'cmd.fork.unsupported_backend') {
3398
+ const cliName = getCliDisplayName((ds.session.cliId ?? getBot(forkAppId).config.cliId ?? 'claude-code'));
3399
+ await sessionReply(rootId, t(errKey, { cli: cliName }, loc));
3400
+ }
3401
+ else if (errKey) {
3402
+ await sessionReply(rootId, t(errKey, undefined, loc));
3403
+ }
3404
+ else {
3405
+ await sessionReply(rootId, t('cmd.fork.failed', { error: result.error }, loc));
3406
+ }
3407
+ if (result.orphanTopic) {
3408
+ await sessionReply(rootId, t('cmd.fork.orphan_topic_left', undefined, loc));
3409
+ }
3410
+ break;
3411
+ }
3412
+ await sessionReply(rootId, t('cmd.fork.subtopic_created', { link: result.link }, loc));
3413
+ logger.info(`[${logTag}] /fork sub-topic completed: child=${result.childSessionId.substring(0, 8)} anchor=${result.anchorId.substring(0, 12)} (source ${ds.session.sessionId.substring(0, 8)} untouched)`);
3414
+ break;
3415
+ }
3416
+ // ── /fork --create <群名> @bot ──────────────────────────────────────
3417
+ const afterFlag = argsLine.replace(/^--create\s*/i, '').trim();
3378
3418
  // Resolve the bot to invite into the new group. Fork copies THIS
3379
3419
  // session's transcript, so the child MUST run the same bot as the
3380
3420
  // source — i.e. the invited bot is always this bot. Therefore:
@@ -3489,6 +3529,14 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
3489
3529
  logger.info(`[${logTag}] /fork --create completed: chat=${forkChatId} child=${forkResult.childSessionId.substring(0, 8)} bot=${targetBotAppId} (source ${ds.session.sessionId.substring(0, 8)} untouched)`);
3490
3530
  break;
3491
3531
  }
3532
+ case '/forklist': {
3533
+ if (!ds) {
3534
+ await sessionReply(rootId, t('cmd.fork.no_session', undefined, loc));
3535
+ break;
3536
+ }
3537
+ await upsertForkPanelCard(ds, loc, { allowEmpty: true });
3538
+ break;
3539
+ }
3492
3540
  case '/card': {
3493
3541
  // Existing-session path. New topics route /card via handleCardCommand at
3494
3542
  // the router (so no phantom session is created). off/on work without a
@@ -3607,6 +3655,7 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
3607
3655
  t('help.relay', undefined, loc),
3608
3656
  t('help.relay_create', undefined, loc),
3609
3657
  t('help.fork', undefined, loc),
3658
+ t('help.forklist', undefined, loc),
3610
3659
  '',
3611
3660
  t('help.heading_login', undefined, loc),
3612
3661
  t('help.login', undefined, loc),
@@ -3897,4 +3946,163 @@ export async function startResumeImportSession(target, ds, deps, larkAppId) {
3897
3946
  const cliName = sessionCliDisplayName(ds);
3898
3947
  await sessionReply(sessionAnchorId(ds), t('cmd.adopt.resume_success', { cliName, project, title: target.title || target.cliSessionId.slice(0, 8) }, loc));
3899
3948
  }
3949
+ /** Fork the current session into a new sub-topic of the same topic group.
3950
+ * The session copy itself stays in worker-pool's generic `forkSession()`;
3951
+ * this layer only creates the Lark destination, supplies the first task turn,
3952
+ * and records display-only lineage for the parent panel. */
3953
+ export async function startForkSubtopicSession(taskText, parentDs, message, larkAppId) {
3954
+ const appId = parentDs.larkAppId ?? larkAppId;
3955
+ if (!appId)
3956
+ return { ok: false, error: 'missing_lark_app_id', orphanTopic: false };
3957
+ const loc = localeForBot(appId);
3958
+ const bot = getBot(appId);
3959
+ const botCfg = bot.config;
3960
+ const parentSession = parentDs.session;
3961
+ const chatId = parentDs.chatId;
3962
+ const brand = normalizeBrand(botCfg.brand);
3963
+ const taskTitle = taskText.split(/\r?\n/).map(line => line.trim()).find(Boolean)?.slice(0, 60)
3964
+ ?? taskText.slice(0, 60);
3965
+ const senderIsBot = message.senderType === 'app' || message.senderType === 'bot';
3966
+ const triggerSender = {
3967
+ openId: message.senderId,
3968
+ type: senderIsBot ? 'bot' : 'user',
3969
+ ...(message.senderName ? { name: message.senderName } : {}),
3970
+ };
3971
+ let anchorId;
3972
+ const recallAnchor = async () => {
3973
+ if (!anchorId)
3974
+ return true;
3975
+ try {
3976
+ return await deleteMessage(appId, anchorId);
3977
+ }
3978
+ catch (err) {
3979
+ logger.warn(`[${parentSession.sessionId.substring(0, 8)}] /fork sub-topic recall failed: `
3980
+ + `${err instanceof Error ? err.message : String(err)}`);
3981
+ return false;
3982
+ }
3983
+ };
3984
+ try {
3985
+ let parentThreadId = parentSession.larkThreadId ?? message.threadId;
3986
+ if (!parentThreadId) {
3987
+ parentThreadId = (await getMessageThreadId(appId, parentSession.rootMessageId)) ?? undefined;
3988
+ }
3989
+ if (parentThreadId && parentSession.larkThreadId !== parentThreadId) {
3990
+ parentSession.larkThreadId = parentThreadId;
3991
+ sessionStore.updateSession(parentSession);
3992
+ }
3993
+ const parentLink = parentThreadId
3994
+ ? threadAppLink(chatId, parentThreadId, brand)
3995
+ : chatAppLink(chatId, brand);
3996
+ const localeKey = loc === 'en' ? 'en_us' : 'zh_cn';
3997
+ const seedPost = JSON.stringify({
3998
+ [localeKey]: {
3999
+ title: `${t('cmd.fork.badge', undefined, loc)} ${taskText.replace(/\s*\n+\s*/g, ' ').slice(0, 300)}`,
4000
+ content: [[
4001
+ ...(senderIsBot ? [] : [{ tag: 'at', user_id: message.senderId }]),
4002
+ {
4003
+ tag: 'text',
4004
+ text: `${senderIsBot ? '' : ' '}${t('cmd.fork.seed_parent_line', { title: parentSession.title || '' }, loc)} `,
4005
+ },
4006
+ { tag: 'a', text: t('cmd.fork.seed_back_link', undefined, loc), href: parentLink },
4007
+ ]],
4008
+ },
4009
+ });
4010
+ anchorId = await sendMessage(appId, chatId, seedPost, 'post');
4011
+ const childThreadId = (await getMessageThreadId(appId, anchorId)) ?? undefined;
4012
+ const childIntro = t('cmd.fork.child_intro', {
4013
+ parentTitle: parentSession.title || '',
4014
+ parentSessionId: parentSession.sessionId,
4015
+ parentRootId: parentSession.rootMessageId,
4016
+ }, loc);
4017
+ const availableBots = await getAvailableBots(appId, chatId);
4018
+ const childCliId = parentSession.cliId ?? botCfg.cliId;
4019
+ const { forkSession } = await import('./worker-pool.js');
4020
+ const forkResult = await forkSession(parentSession.sessionId, chatId, anchorId, 'group', 'thread', {
4021
+ childTitle: `${t('cmd.fork.badge', undefined, loc)} ${taskTitle}`,
4022
+ forkTaskText: taskText,
4023
+ larkThreadId: childThreadId,
4024
+ turnId: message.messageId,
4025
+ senderOpenId: triggerSender.openId,
4026
+ senderIsBot,
4027
+ buildInitialPrompt: childSessionId => buildNewTopicCliInput(`${childIntro}\n\n${taskText}`, childSessionId, childCliId, botCfg.cliPathOverride, undefined, undefined, availableBots, undefined, { name: bot.botName, openId: bot.botOpenId }, loc, triggerSender, { larkAppId: appId, chatId }),
4028
+ });
4029
+ if (!forkResult.ok) {
4030
+ const orphanTopic = !await recallAnchor();
4031
+ return { ok: false, error: forkResult.error, orphanTopic };
4032
+ }
4033
+ if (!parentSession.forkChildSessionIds?.includes(forkResult.childSessionId)) {
4034
+ parentSession.forkChildSessionIds = [
4035
+ ...(parentSession.forkChildSessionIds ?? []),
4036
+ forkResult.childSessionId,
4037
+ ];
4038
+ try {
4039
+ sessionStore.updateSession(parentSession);
4040
+ }
4041
+ catch (err) {
4042
+ logger.warn(`[${parentSession.sessionId.substring(0, 8)}] /fork parent lineage update failed: `
4043
+ + `${err instanceof Error ? err.message : String(err)}`);
4044
+ }
4045
+ }
4046
+ try {
4047
+ await upsertForkPanelCard(parentDs, loc);
4048
+ }
4049
+ catch (err) {
4050
+ logger.warn(`[${parentSession.sessionId.substring(0, 8)}] /fork panel refresh failed: `
4051
+ + `${err instanceof Error ? err.message : String(err)}`);
4052
+ }
4053
+ return {
4054
+ ok: true,
4055
+ childSessionId: forkResult.childSessionId,
4056
+ anchorId,
4057
+ link: childThreadId ? threadAppLink(chatId, childThreadId, brand) : chatAppLink(chatId, brand),
4058
+ };
4059
+ }
4060
+ catch (err) {
4061
+ logger.error(`[${parentSession.sessionId.substring(0, 8)}] /fork sub-topic failed: `
4062
+ + `${err instanceof Error ? err.message : String(err)}`);
4063
+ return {
4064
+ ok: false,
4065
+ error: anchorId ? 'fork_subtopic_failed' : 'topic_creation_failed',
4066
+ orphanTopic: anchorId ? !await recallAnchor() : false,
4067
+ };
4068
+ }
4069
+ }
4070
+ /** Re-post the parent session's fork panel at the bottom of the topic. Reading
4071
+ * each child row from the store keeps `/forklist` status current without
4072
+ * coupling child lifecycle to its parent. */
4073
+ async function upsertForkPanelCard(parentDs, loc, opts) {
4074
+ const appId = parentDs.larkAppId;
4075
+ const chatId = parentDs.chatId;
4076
+ const brand = normalizeBrand(getBot(appId).config.brand);
4077
+ const children = (parentDs.session.forkChildSessionIds ?? [])
4078
+ .map(sessionId => sessionStore.getSession(sessionId))
4079
+ .filter((session) => !!session)
4080
+ .map(session => ({
4081
+ instruction: session.forkTaskText ?? session.title,
4082
+ status: (session.status === 'active' ? 'active' : 'closed'),
4083
+ link: session.larkThreadId
4084
+ ? threadAppLink(chatId, session.larkThreadId, brand)
4085
+ : chatAppLink(chatId, brand),
4086
+ }));
4087
+ if (children.length === 0 && !opts?.allowEmpty)
4088
+ return;
4089
+ const staleCardId = parentDs.session.forkPanelCardId;
4090
+ if (staleCardId) {
4091
+ try {
4092
+ await deleteMessage(appId, staleCardId);
4093
+ }
4094
+ catch {
4095
+ // It may already be withdrawn or past Lark's recall window. Posting the
4096
+ // fresh panel is still more useful than keeping the command silent.
4097
+ }
4098
+ }
4099
+ try {
4100
+ const cardId = await replyMessage(appId, parentDs.session.rootMessageId, buildForkPanelCard(children, loc), 'interactive', true);
4101
+ parentDs.session.forkPanelCardId = cardId;
4102
+ sessionStore.updateSession(parentDs.session);
4103
+ }
4104
+ catch (err) {
4105
+ logger.warn(`[fork-panel] failed to post panel card: ${err instanceof Error ? err.message : err}`);
4106
+ }
4107
+ }
3900
4108
  //# sourceMappingURL=command-handler.js.map