botmux 2.46.1 → 2.47.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 (468) hide show
  1. package/README.en.md +5 -10
  2. package/README.md +5 -10
  3. package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
  4. package/dist/adapters/backend/tmux-backend.js +0 -11
  5. package/dist/adapters/backend/tmux-backend.js.map +1 -1
  6. package/dist/adapters/cli/claude-code.d.ts.map +1 -1
  7. package/dist/adapters/cli/claude-code.js +9 -36
  8. package/dist/adapters/cli/claude-code.js.map +1 -1
  9. package/dist/adapters/cli/coco.d.ts.map +1 -1
  10. package/dist/adapters/cli/coco.js +1 -26
  11. package/dist/adapters/cli/coco.js.map +1 -1
  12. package/dist/adapters/cli/codex.d.ts.map +1 -1
  13. package/dist/adapters/cli/codex.js +1 -6
  14. package/dist/adapters/cli/codex.js.map +1 -1
  15. package/dist/adapters/cli/cursor.d.ts.map +1 -1
  16. package/dist/adapters/cli/cursor.js +12 -58
  17. package/dist/adapters/cli/cursor.js.map +1 -1
  18. package/dist/adapters/cli/gemini.d.ts.map +1 -1
  19. package/dist/adapters/cli/gemini.js +1 -5
  20. package/dist/adapters/cli/gemini.js.map +1 -1
  21. package/dist/adapters/cli/opencode.d.ts.map +1 -1
  22. package/dist/adapters/cli/opencode.js +1 -19
  23. package/dist/adapters/cli/opencode.js.map +1 -1
  24. package/dist/adapters/cli/registry.d.ts +1 -5
  25. package/dist/adapters/cli/registry.d.ts.map +1 -1
  26. package/dist/adapters/cli/registry.js +2 -22
  27. package/dist/adapters/cli/registry.js.map +1 -1
  28. package/dist/adapters/cli/shared-hints.d.ts +1 -1
  29. package/dist/adapters/cli/shared-hints.d.ts.map +1 -1
  30. package/dist/adapters/cli/shared-hints.js +1 -2
  31. package/dist/adapters/cli/shared-hints.js.map +1 -1
  32. package/dist/adapters/cli/types.d.ts +2 -35
  33. package/dist/adapters/cli/types.d.ts.map +1 -1
  34. package/dist/bot-registry.d.ts +0 -59
  35. package/dist/bot-registry.d.ts.map +1 -1
  36. package/dist/bot-registry.js +0 -67
  37. package/dist/bot-registry.js.map +1 -1
  38. package/dist/cli/bots-list-output.d.ts +0 -8
  39. package/dist/cli/bots-list-output.d.ts.map +1 -1
  40. package/dist/cli/bots-list-output.js +0 -9
  41. package/dist/cli/bots-list-output.js.map +1 -1
  42. package/dist/cli.d.ts +1 -15
  43. package/dist/cli.d.ts.map +1 -1
  44. package/dist/cli.js +106 -603
  45. package/dist/cli.js.map +1 -1
  46. package/dist/config.d.ts +2 -11
  47. package/dist/config.d.ts.map +1 -1
  48. package/dist/config.js +4 -17
  49. package/dist/config.js.map +1 -1
  50. package/dist/core/command-handler.d.ts +0 -20
  51. package/dist/core/command-handler.d.ts.map +1 -1
  52. package/dist/core/command-handler.js +313 -762
  53. package/dist/core/command-handler.js.map +1 -1
  54. package/dist/core/dashboard-ipc-server.d.ts +0 -2
  55. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  56. package/dist/core/dashboard-ipc-server.js +2 -222
  57. package/dist/core/dashboard-ipc-server.js.map +1 -1
  58. package/dist/core/role-resolver.d.ts +1 -17
  59. package/dist/core/role-resolver.d.ts.map +1 -1
  60. package/dist/core/role-resolver.js +10 -64
  61. package/dist/core/role-resolver.js.map +1 -1
  62. package/dist/core/session-discovery.d.ts.map +1 -1
  63. package/dist/core/session-discovery.js +5 -19
  64. package/dist/core/session-discovery.js.map +1 -1
  65. package/dist/core/session-manager.d.ts +1 -1
  66. package/dist/core/session-manager.d.ts.map +1 -1
  67. package/dist/core/session-manager.js +20 -37
  68. package/dist/core/session-manager.js.map +1 -1
  69. package/dist/core/types.d.ts +0 -5
  70. package/dist/core/types.d.ts.map +1 -1
  71. package/dist/core/types.js.map +1 -1
  72. package/dist/core/worker-pool.d.ts +0 -141
  73. package/dist/core/worker-pool.d.ts.map +1 -1
  74. package/dist/core/worker-pool.js +24 -543
  75. package/dist/core/worker-pool.js.map +1 -1
  76. package/dist/daemon.d.ts.map +1 -1
  77. package/dist/daemon.js +58 -213
  78. package/dist/daemon.js.map +1 -1
  79. package/dist/dashboard/auth.d.ts +1 -6
  80. package/dist/dashboard/auth.d.ts.map +1 -1
  81. package/dist/dashboard/auth.js +1 -9
  82. package/dist/dashboard/auth.js.map +1 -1
  83. package/dist/dashboard/web/app.js +0 -8
  84. package/dist/dashboard/web/app.js.map +1 -1
  85. package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
  86. package/dist/dashboard/web/bot-defaults.js +21 -205
  87. package/dist/dashboard/web/bot-defaults.js.map +1 -1
  88. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  89. package/dist/dashboard/web/i18n.js +5 -43
  90. package/dist/dashboard/web/i18n.js.map +1 -1
  91. package/dist/dashboard/web/sessions.d.ts.map +1 -1
  92. package/dist/dashboard/web/sessions.js +0 -4
  93. package/dist/dashboard/web/sessions.js.map +1 -1
  94. package/dist/dashboard/web/workflows.js +3 -3
  95. package/dist/dashboard/web/workflows.js.map +1 -1
  96. package/dist/dashboard/workflow-api.d.ts +1 -8
  97. package/dist/dashboard/workflow-api.d.ts.map +1 -1
  98. package/dist/dashboard/workflow-api.js +4 -19
  99. package/dist/dashboard/workflow-api.js.map +1 -1
  100. package/dist/dashboard-web/app.js +375 -539
  101. package/dist/dashboard-web/index.html +1 -3
  102. package/dist/dashboard-web/style.css +0 -22
  103. package/dist/dashboard.js +2 -199
  104. package/dist/dashboard.js.map +1 -1
  105. package/dist/i18n/en.d.ts.map +1 -1
  106. package/dist/i18n/en.js +11 -104
  107. package/dist/i18n/en.js.map +1 -1
  108. package/dist/i18n/zh.d.ts.map +1 -1
  109. package/dist/i18n/zh.js +11 -104
  110. package/dist/i18n/zh.js.map +1 -1
  111. package/dist/im/lark/card-builder.d.ts +3 -108
  112. package/dist/im/lark/card-builder.d.ts.map +1 -1
  113. package/dist/im/lark/card-builder.js +50 -480
  114. package/dist/im/lark/card-builder.js.map +1 -1
  115. package/dist/im/lark/card-handler.d.ts.map +1 -1
  116. package/dist/im/lark/card-handler.js +18 -241
  117. package/dist/im/lark/card-handler.js.map +1 -1
  118. package/dist/im/lark/client.d.ts +0 -83
  119. package/dist/im/lark/client.d.ts.map +1 -1
  120. package/dist/im/lark/client.js +70 -286
  121. package/dist/im/lark/client.js.map +1 -1
  122. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  123. package/dist/im/lark/event-dispatcher.js +4 -29
  124. package/dist/im/lark/event-dispatcher.js.map +1 -1
  125. package/dist/im/lark/grant-command.d.ts +1 -2
  126. package/dist/im/lark/grant-command.d.ts.map +1 -1
  127. package/dist/im/lark/grant-command.js +2 -3
  128. package/dist/im/lark/grant-command.js.map +1 -1
  129. package/dist/im/lark/identity-cache.d.ts.map +1 -1
  130. package/dist/im/lark/identity-cache.js +3 -3
  131. package/dist/im/lark/identity-cache.js.map +1 -1
  132. package/dist/im/lark/md-card.d.ts +2 -20
  133. package/dist/im/lark/md-card.d.ts.map +1 -1
  134. package/dist/im/lark/md-card.js +17 -49
  135. package/dist/im/lark/md-card.js.map +1 -1
  136. package/dist/im/lark/message-parser.d.ts.map +1 -1
  137. package/dist/im/lark/message-parser.js +31 -87
  138. package/dist/im/lark/message-parser.js.map +1 -1
  139. package/dist/im/lark/workflow-card-handler.d.ts +2 -2
  140. package/dist/im/lark/workflow-card-handler.d.ts.map +1 -1
  141. package/dist/im/lark/workflow-card-handler.js +1 -12
  142. package/dist/im/lark/workflow-card-handler.js.map +1 -1
  143. package/dist/im/lark/workflow-progress-card.d.ts.map +1 -1
  144. package/dist/im/lark/workflow-progress-card.js +0 -53
  145. package/dist/im/lark/workflow-progress-card.js.map +1 -1
  146. package/dist/services/codex-bridge-queue.d.ts +0 -1
  147. package/dist/services/codex-bridge-queue.d.ts.map +1 -1
  148. package/dist/services/codex-bridge-queue.js +0 -23
  149. package/dist/services/codex-bridge-queue.js.map +1 -1
  150. package/dist/services/codex-transcript.d.ts +0 -1
  151. package/dist/services/codex-transcript.d.ts.map +1 -1
  152. package/dist/services/codex-transcript.js.map +1 -1
  153. package/dist/services/feishu-task-client.d.ts +28 -0
  154. package/dist/services/feishu-task-client.d.ts.map +1 -0
  155. package/dist/services/feishu-task-client.js +123 -0
  156. package/dist/services/feishu-task-client.js.map +1 -0
  157. package/dist/services/grant-store.d.ts +2 -12
  158. package/dist/services/grant-store.d.ts.map +1 -1
  159. package/dist/services/grant-store.js +4 -51
  160. package/dist/services/grant-store.js.map +1 -1
  161. package/dist/services/group-creator.d.ts +0 -10
  162. package/dist/services/group-creator.d.ts.map +1 -1
  163. package/dist/services/group-creator.js +1 -26
  164. package/dist/services/group-creator.js.map +1 -1
  165. package/dist/services/groups-store.d.ts +0 -30
  166. package/dist/services/groups-store.d.ts.map +1 -1
  167. package/dist/services/groups-store.js +12 -85
  168. package/dist/services/groups-store.js.map +1 -1
  169. package/dist/services/project-scanner.d.ts +0 -10
  170. package/dist/services/project-scanner.d.ts.map +1 -1
  171. package/dist/services/project-scanner.js +0 -11
  172. package/dist/services/project-scanner.js.map +1 -1
  173. package/dist/services/session-store.js +1 -1
  174. package/dist/services/session-store.js.map +1 -1
  175. package/dist/services/task-store.d.ts +37 -0
  176. package/dist/services/task-store.d.ts.map +1 -0
  177. package/dist/services/task-store.js +115 -0
  178. package/dist/services/task-store.js.map +1 -0
  179. package/dist/setup/bot-config-editor.d.ts +1 -8
  180. package/dist/setup/bot-config-editor.d.ts.map +1 -1
  181. package/dist/setup/bot-config-editor.js +2 -20
  182. package/dist/setup/bot-config-editor.js.map +1 -1
  183. package/dist/setup/ensure-tmux.d.ts +22 -0
  184. package/dist/setup/ensure-tmux.d.ts.map +1 -1
  185. package/dist/setup/ensure-tmux.js +1 -25
  186. package/dist/setup/ensure-tmux.js.map +1 -1
  187. package/dist/setup/verify-permissions.d.ts.map +1 -1
  188. package/dist/setup/verify-permissions.js +1 -15
  189. package/dist/setup/verify-permissions.js.map +1 -1
  190. package/dist/skills/definitions.d.ts +0 -2
  191. package/dist/skills/definitions.d.ts.map +1 -1
  192. package/dist/skills/definitions.js +12 -178
  193. package/dist/skills/definitions.js.map +1 -1
  194. package/dist/skills/installer.d.ts +0 -34
  195. package/dist/skills/installer.d.ts.map +1 -1
  196. package/dist/skills/installer.js +2 -119
  197. package/dist/skills/installer.js.map +1 -1
  198. package/dist/types.d.ts +0 -25
  199. package/dist/types.d.ts.map +1 -1
  200. package/dist/utils/bot-routing.d.ts +0 -50
  201. package/dist/utils/bot-routing.d.ts.map +1 -1
  202. package/dist/utils/bot-routing.js +0 -83
  203. package/dist/utils/bot-routing.js.map +1 -1
  204. package/dist/utils/user-token.d.ts.map +1 -1
  205. package/dist/utils/user-token.js +2 -0
  206. package/dist/utils/user-token.js.map +1 -1
  207. package/dist/worker.js +27 -198
  208. package/dist/worker.js.map +1 -1
  209. package/dist/workflows/attempt-resume.d.ts.map +1 -1
  210. package/dist/workflows/attempt-resume.js +2 -2
  211. package/dist/workflows/attempt-resume.js.map +1 -1
  212. package/dist/workflows/definition.d.ts +9 -412
  213. package/dist/workflows/definition.d.ts.map +1 -1
  214. package/dist/workflows/definition.js +3 -238
  215. package/dist/workflows/definition.js.map +1 -1
  216. package/dist/workflows/events/payloads.d.ts +11 -114
  217. package/dist/workflows/events/payloads.d.ts.map +1 -1
  218. package/dist/workflows/events/payloads.js +0 -46
  219. package/dist/workflows/events/payloads.js.map +1 -1
  220. package/dist/workflows/events/replay.d.ts +0 -21
  221. package/dist/workflows/events/replay.d.ts.map +1 -1
  222. package/dist/workflows/events/replay.js +0 -103
  223. package/dist/workflows/events/replay.js.map +1 -1
  224. package/dist/workflows/events/schema.d.ts +1017 -1712
  225. package/dist/workflows/events/schema.d.ts.map +1 -1
  226. package/dist/workflows/events/schema.js +1 -37
  227. package/dist/workflows/events/schema.js.map +1 -1
  228. package/dist/workflows/events/types.d.ts +1 -5
  229. package/dist/workflows/events/types.d.ts.map +1 -1
  230. package/dist/workflows/loader.d.ts +0 -14
  231. package/dist/workflows/loader.d.ts.map +1 -1
  232. package/dist/workflows/loader.js +0 -27
  233. package/dist/workflows/loader.js.map +1 -1
  234. package/dist/workflows/loop.js +0 -58
  235. package/dist/workflows/loop.js.map +1 -1
  236. package/dist/workflows/ops-projection.d.ts +0 -58
  237. package/dist/workflows/ops-projection.d.ts.map +1 -1
  238. package/dist/workflows/ops-projection.js +0 -74
  239. package/dist/workflows/ops-projection.js.map +1 -1
  240. package/dist/workflows/orchestrator.d.ts +1 -65
  241. package/dist/workflows/orchestrator.d.ts.map +1 -1
  242. package/dist/workflows/orchestrator.js +74 -486
  243. package/dist/workflows/orchestrator.js.map +1 -1
  244. package/dist/workflows/output-binding.d.ts +1 -8
  245. package/dist/workflows/output-binding.d.ts.map +1 -1
  246. package/dist/workflows/output-binding.js +11 -75
  247. package/dist/workflows/output-binding.js.map +1 -1
  248. package/dist/workflows/runtime.d.ts +1 -1
  249. package/dist/workflows/runtime.d.ts.map +1 -1
  250. package/dist/workflows/runtime.js +4 -39
  251. package/dist/workflows/runtime.js.map +1 -1
  252. package/dist/workflows/wait.d.ts +2 -23
  253. package/dist/workflows/wait.d.ts.map +1 -1
  254. package/dist/workflows/wait.js +17 -39
  255. package/dist/workflows/wait.js.map +1 -1
  256. package/package.json +1 -1
  257. package/dist/adapters/adopt-route.d.ts +0 -63
  258. package/dist/adapters/adopt-route.d.ts.map +0 -1
  259. package/dist/adapters/adopt-route.js +0 -195
  260. package/dist/adapters/adopt-route.js.map +0 -1
  261. package/dist/adapters/cli/codex-app.d.ts +0 -4
  262. package/dist/adapters/cli/codex-app.d.ts.map +0 -1
  263. package/dist/adapters/cli/codex-app.js +0 -72
  264. package/dist/adapters/cli/codex-app.js.map +0 -1
  265. package/dist/adapters/cli/hermes.d.ts +0 -4
  266. package/dist/adapters/cli/hermes.d.ts.map +0 -1
  267. package/dist/adapters/cli/hermes.js +0 -40
  268. package/dist/adapters/cli/hermes.js.map +0 -1
  269. package/dist/adapters/cli/mira.d.ts +0 -4
  270. package/dist/adapters/cli/mira.d.ts.map +0 -1
  271. package/dist/adapters/cli/mira.js +0 -67
  272. package/dist/adapters/cli/mira.js.map +0 -1
  273. package/dist/adapters/cli/mtr.d.ts +0 -5
  274. package/dist/adapters/cli/mtr.d.ts.map +0 -1
  275. package/dist/adapters/cli/mtr.js +0 -62
  276. package/dist/adapters/cli/mtr.js.map +0 -1
  277. package/dist/adapters/hook-command.d.ts +0 -18
  278. package/dist/adapters/hook-command.d.ts.map +0 -1
  279. package/dist/adapters/hook-command.js +0 -38
  280. package/dist/adapters/hook-command.js.map +0 -1
  281. package/dist/adapters/hook-installer.d.ts +0 -14
  282. package/dist/adapters/hook-installer.d.ts.map +0 -1
  283. package/dist/adapters/hook-installer.js +0 -192
  284. package/dist/adapters/hook-installer.js.map +0 -1
  285. package/dist/codex-app-runner.d.ts +0 -3
  286. package/dist/codex-app-runner.d.ts.map +0 -1
  287. package/dist/codex-app-runner.js +0 -512
  288. package/dist/codex-app-runner.js.map +0 -1
  289. package/dist/core/ask-api.d.ts +0 -47
  290. package/dist/core/ask-api.d.ts.map +0 -1
  291. package/dist/core/ask-api.js +0 -139
  292. package/dist/core/ask-api.js.map +0 -1
  293. package/dist/core/ask-args.d.ts +0 -53
  294. package/dist/core/ask-args.d.ts.map +0 -1
  295. package/dist/core/ask-args.js +0 -122
  296. package/dist/core/ask-args.js.map +0 -1
  297. package/dist/core/ask-broker.d.ts +0 -98
  298. package/dist/core/ask-broker.d.ts.map +0 -1
  299. package/dist/core/ask-broker.js +0 -329
  300. package/dist/core/ask-broker.js.map +0 -1
  301. package/dist/core/ask-hook/claude-code.d.ts +0 -50
  302. package/dist/core/ask-hook/claude-code.d.ts.map +0 -1
  303. package/dist/core/ask-hook/claude-code.js +0 -145
  304. package/dist/core/ask-hook/claude-code.js.map +0 -1
  305. package/dist/core/ask-hook/codex.d.ts +0 -43
  306. package/dist/core/ask-hook/codex.d.ts.map +0 -1
  307. package/dist/core/ask-hook/codex.js +0 -69
  308. package/dist/core/ask-hook/codex.js.map +0 -1
  309. package/dist/core/ask-hook/opencode.d.ts +0 -41
  310. package/dist/core/ask-hook/opencode.d.ts.map +0 -1
  311. package/dist/core/ask-hook/opencode.js +0 -108
  312. package/dist/core/ask-hook/opencode.js.map +0 -1
  313. package/dist/core/ask-hook/registry.d.ts +0 -3
  314. package/dist/core/ask-hook/registry.d.ts.map +0 -1
  315. package/dist/core/ask-hook/registry.js +0 -12
  316. package/dist/core/ask-hook/registry.js.map +0 -1
  317. package/dist/core/ask-hook/types.d.ts +0 -26
  318. package/dist/core/ask-hook/types.d.ts.map +0 -1
  319. package/dist/core/ask-hook/types.js +0 -2
  320. package/dist/core/ask-hook/types.js.map +0 -1
  321. package/dist/core/ask-types.d.ts +0 -146
  322. package/dist/core/ask-types.d.ts.map +0 -1
  323. package/dist/core/ask-types.js +0 -18
  324. package/dist/core/ask-types.js.map +0 -1
  325. package/dist/core/trigger-session.d.ts +0 -9
  326. package/dist/core/trigger-session.d.ts.map +0 -1
  327. package/dist/core/trigger-session.js +0 -158
  328. package/dist/core/trigger-session.js.map +0 -1
  329. package/dist/dashboard/connector-api.d.ts +0 -3
  330. package/dist/dashboard/connector-api.d.ts.map +0 -1
  331. package/dist/dashboard/connector-api.js +0 -351
  332. package/dist/dashboard/connector-api.js.map +0 -1
  333. package/dist/dashboard/federated-group-core.d.ts +0 -54
  334. package/dist/dashboard/federated-group-core.d.ts.map +0 -1
  335. package/dist/dashboard/federated-group-core.js +0 -165
  336. package/dist/dashboard/federated-group-core.js.map +0 -1
  337. package/dist/dashboard/federation-api.d.ts +0 -42
  338. package/dist/dashboard/federation-api.d.ts.map +0 -1
  339. package/dist/dashboard/federation-api.js +0 -408
  340. package/dist/dashboard/federation-api.js.map +0 -1
  341. package/dist/dashboard/federation-spoke-api.d.ts +0 -76
  342. package/dist/dashboard/federation-spoke-api.d.ts.map +0 -1
  343. package/dist/dashboard/federation-spoke-api.js +0 -618
  344. package/dist/dashboard/federation-spoke-api.js.map +0 -1
  345. package/dist/dashboard/team-group.d.ts +0 -18
  346. package/dist/dashboard/team-group.d.ts.map +0 -1
  347. package/dist/dashboard/team-group.js +0 -7
  348. package/dist/dashboard/team-group.js.map +0 -1
  349. package/dist/dashboard/trigger-api.d.ts +0 -13
  350. package/dist/dashboard/trigger-api.d.ts.map +0 -1
  351. package/dist/dashboard/trigger-api.js +0 -77
  352. package/dist/dashboard/trigger-api.js.map +0 -1
  353. package/dist/dashboard/web/connectors.d.ts +0 -2
  354. package/dist/dashboard/web/connectors.d.ts.map +0 -1
  355. package/dist/dashboard/web/connectors.js +0 -187
  356. package/dist/dashboard/web/connectors.js.map +0 -1
  357. package/dist/dashboard/web/team-federation.d.ts +0 -3
  358. package/dist/dashboard/web/team-federation.d.ts.map +0 -1
  359. package/dist/dashboard/web/team-federation.js +0 -487
  360. package/dist/dashboard/web/team-federation.js.map +0 -1
  361. package/dist/dashboard/webhook-routes.d.ts +0 -19
  362. package/dist/dashboard/webhook-routes.d.ts.map +0 -1
  363. package/dist/dashboard/webhook-routes.js +0 -321
  364. package/dist/dashboard/webhook-routes.js.map +0 -1
  365. package/dist/im/lark/ask-card.d.ts +0 -55
  366. package/dist/im/lark/ask-card.d.ts.map +0 -1
  367. package/dist/im/lark/ask-card.js +0 -328
  368. package/dist/im/lark/ask-card.js.map +0 -1
  369. package/dist/mira-output.d.ts +0 -3
  370. package/dist/mira-output.d.ts.map +0 -1
  371. package/dist/mira-output.js +0 -136
  372. package/dist/mira-output.js.map +0 -1
  373. package/dist/mira-runner.d.ts +0 -3
  374. package/dist/mira-runner.d.ts.map +0 -1
  375. package/dist/mira-runner.js +0 -534
  376. package/dist/mira-runner.js.map +0 -1
  377. package/dist/services/bot-owner-store.d.ts +0 -28
  378. package/dist/services/bot-owner-store.d.ts.map +0 -1
  379. package/dist/services/bot-owner-store.js +0 -82
  380. package/dist/services/bot-owner-store.js.map +0 -1
  381. package/dist/services/bot-profile-store.d.ts +0 -16
  382. package/dist/services/bot-profile-store.d.ts.map +0 -1
  383. package/dist/services/bot-profile-store.js +0 -98
  384. package/dist/services/bot-profile-store.js.map +0 -1
  385. package/dist/services/brand-store.d.ts +0 -15
  386. package/dist/services/brand-store.d.ts.map +0 -1
  387. package/dist/services/brand-store.js +0 -47
  388. package/dist/services/brand-store.js.map +0 -1
  389. package/dist/services/card-prefs-store.d.ts +0 -20
  390. package/dist/services/card-prefs-store.d.ts.map +0 -1
  391. package/dist/services/card-prefs-store.js +0 -82
  392. package/dist/services/card-prefs-store.js.map +0 -1
  393. package/dist/services/connector-store.d.ts +0 -58
  394. package/dist/services/connector-store.d.ts.map +0 -1
  395. package/dist/services/connector-store.js +0 -79
  396. package/dist/services/connector-store.js.map +0 -1
  397. package/dist/services/deployment-identity.d.ts +0 -22
  398. package/dist/services/deployment-identity.d.ts.map +0 -1
  399. package/dist/services/deployment-identity.js +0 -67
  400. package/dist/services/deployment-identity.js.map +0 -1
  401. package/dist/services/federation-membership-store.d.ts +0 -23
  402. package/dist/services/federation-membership-store.d.ts.map +0 -1
  403. package/dist/services/federation-membership-store.js +0 -66
  404. package/dist/services/federation-membership-store.js.map +0 -1
  405. package/dist/services/federation-roster.d.ts +0 -54
  406. package/dist/services/federation-roster.d.ts.map +0 -1
  407. package/dist/services/federation-roster.js +0 -51
  408. package/dist/services/federation-roster.js.map +0 -1
  409. package/dist/services/federation-store.d.ts +0 -76
  410. package/dist/services/federation-store.d.ts.map +0 -1
  411. package/dist/services/federation-store.js +0 -133
  412. package/dist/services/federation-store.js.map +0 -1
  413. package/dist/services/hermes-transcript.d.ts +0 -7
  414. package/dist/services/hermes-transcript.d.ts.map +0 -1
  415. package/dist/services/hermes-transcript.js +0 -117
  416. package/dist/services/hermes-transcript.js.map +0 -1
  417. package/dist/services/invite-store.d.ts +0 -28
  418. package/dist/services/invite-store.d.ts.map +0 -1
  419. package/dist/services/invite-store.js +0 -85
  420. package/dist/services/invite-store.js.map +0 -1
  421. package/dist/services/pairing-store.d.ts +0 -47
  422. package/dist/services/pairing-store.d.ts.map +0 -1
  423. package/dist/services/pairing-store.js +0 -132
  424. package/dist/services/pairing-store.js.map +0 -1
  425. package/dist/services/relay-picker.d.ts +0 -22
  426. package/dist/services/relay-picker.d.ts.map +0 -1
  427. package/dist/services/relay-picker.js +0 -62
  428. package/dist/services/relay-picker.js.map +0 -1
  429. package/dist/services/send-policy.d.ts +0 -55
  430. package/dist/services/send-policy.d.ts.map +0 -1
  431. package/dist/services/send-policy.js +0 -47
  432. package/dist/services/send-policy.js.map +0 -1
  433. package/dist/services/team-roster.d.ts +0 -38
  434. package/dist/services/team-roster.d.ts.map +0 -1
  435. package/dist/services/team-roster.js +0 -82
  436. package/dist/services/team-roster.js.map +0 -1
  437. package/dist/services/team-store.d.ts +0 -54
  438. package/dist/services/team-store.d.ts.map +0 -1
  439. package/dist/services/team-store.js +0 -156
  440. package/dist/services/team-store.js.map +0 -1
  441. package/dist/services/trigger-log-store.d.ts +0 -46
  442. package/dist/services/trigger-log-store.d.ts.map +0 -1
  443. package/dist/services/trigger-log-store.js +0 -132
  444. package/dist/services/trigger-log-store.js.map +0 -1
  445. package/dist/services/trigger-types.d.ts +0 -57
  446. package/dist/services/trigger-types.d.ts.map +0 -1
  447. package/dist/services/trigger-types.js +0 -28
  448. package/dist/services/trigger-types.js.map +0 -1
  449. package/dist/services/webhook-key.d.ts +0 -16
  450. package/dist/services/webhook-key.d.ts.map +0 -1
  451. package/dist/services/webhook-key.js +0 -123
  452. package/dist/services/webhook-key.js.map +0 -1
  453. package/dist/services/webhook-lifecycle-extractors.d.ts +0 -15
  454. package/dist/services/webhook-lifecycle-extractors.d.ts.map +0 -1
  455. package/dist/services/webhook-lifecycle-extractors.js +0 -59
  456. package/dist/services/webhook-lifecycle-extractors.js.map +0 -1
  457. package/dist/services/webhook-lifecycle-store.d.ts +0 -45
  458. package/dist/services/webhook-lifecycle-store.d.ts.map +0 -1
  459. package/dist/services/webhook-lifecycle-store.js +0 -159
  460. package/dist/services/webhook-lifecycle-store.js.map +0 -1
  461. package/dist/utils/daemon-discovery.d.ts +0 -11
  462. package/dist/utils/daemon-discovery.d.ts.map +0 -1
  463. package/dist/utils/daemon-discovery.js +0 -59
  464. package/dist/utils/daemon-discovery.js.map +0 -1
  465. package/dist/workflows/trigger-from-envelope.d.ts +0 -13
  466. package/dist/workflows/trigger-from-envelope.d.ts.map +0 -1
  467. package/dist/workflows/trigger-from-envelope.js +0 -67
  468. package/dist/workflows/trigger-from-envelope.js.map +0 -1
@@ -7,15 +7,13 @@ import { join, dirname } from 'node:path';
7
7
  import { homedir } from 'node:os';
8
8
  import { readFileSync, writeFileSync, mkdirSync, existsSync, realpathSync } from 'node:fs';
9
9
  import { fileURLToPath } from 'node:url';
10
- import { ensureSkills, ensureAskSkill, ensurePluginSkills, removeGlobalBotmuxSkills } from '../skills/installer.js';
11
- import { installHook } from '../adapters/hook-installer.js';
12
- import { hookCommandFor } from '../adapters/hook-command.js';
10
+ import { ensureSkills } from '../skills/installer.js';
13
11
  import { randomBytes } from 'node:crypto';
14
12
  import { config } from '../config.js';
15
13
  import * as sessionStore from '../services/session-store.js';
16
14
  import { persistStreamCardState } from './session-manager.js';
17
- import { updateMessage, deleteMessage, sendEphemeralCard, MessageWithdrawnError } from '../im/lark/client.js';
18
- import { buildStreamingCard, buildPrivateSnapshotCard, buildSessionCard, buildTuiPromptCard, buildTuiPromptResolvedCard, buildRelayedFrozenCard, getCliDisplayName } from '../im/lark/card-builder.js';
15
+ import { updateMessage, deleteMessage, MessageWithdrawnError } from '../im/lark/client.js';
16
+ import { buildStreamingCard, buildSessionCard, buildTuiPromptCard, buildTuiPromptResolvedCard, getCliDisplayName } from '../im/lark/card-builder.js';
19
17
  import { loadFrozenCards, saveFrozenCards } from '../services/frozen-card-store.js';
20
18
  import { logger } from '../utils/logger.js';
21
19
  import { createCliAdapterSync } from '../adapters/cli/registry.js';
@@ -23,10 +21,9 @@ import { botLocale, localeForBot, t as tr } from '../i18n/index.js';
23
21
  import { claudeJsonlPathForSession } from '../adapters/cli/claude-code.js';
24
22
  import { buildMarkdownCard, buildContextualReplyCard } from '../im/lark/md-card.js';
25
23
  import { TmuxBackend } from '../adapters/backend/tmux-backend.js';
26
- import { getBot, getAllBots, resolveBrandLabel } from '../bot-registry.js';
24
+ import { getBot, getAllBots } from '../bot-registry.js';
27
25
  import { dashboardEventBus } from './dashboard-events.js';
28
26
  import { composeRowFromActive } from './dashboard-rows.js';
29
- import { knownBotOpenIdsFromCrossRef } from '../utils/bot-routing.js';
30
27
  import { sessionKey, sessionAnchorId } from './types.js';
31
28
  import { usageLimitStateKey } from '../utils/cli-usage-limit.js';
32
29
  const __filename = fileURLToPath(import.meta.url);
@@ -74,84 +71,6 @@ export function findActiveBySessionId(sessionId) {
74
71
  export function getActiveSessionsRegistry() {
75
72
  return activeSessionsRegistry;
76
73
  }
77
- // ─── "Real relayable session" predicate ─────────────────────────────────────
78
- /**
79
- * True iff this DaemonSession represents a real CLI-backed conversation
80
- * that's safe to migrate via /relay. Returns false for daemon-command
81
- * scratch placeholders (the `worker:null + hasHistory:false` records that
82
- * daemon.ts creates for /help, an unfinished picker /relay, etc.) — those
83
- * have no CLI history, no tmux, and migrating them yields an empty shell
84
- * in the target chat with a fake "已就绪" M1.
85
- *
86
- * Why not just `!!ds.worker || ds.hasHistory`:
87
- * - `ds.worker` is runtime-only; null after daemon restart until
88
- * forkWorker re-attaches.
89
- * - `ds.hasHistory` is a runtime field too — restoreActiveSessions sets
90
- * it `true` UNCONDITIONALLY for any persisted non-adopt session
91
- * (session-manager.ts:618). A scratch that survived a restart comes
92
- * back with hasHistory:true, defeating the guard.
93
- *
94
- * Use persisted markers instead: `ds.session.cliId` and
95
- * `ds.session.lastCliInput` are written ONLY after a real worker started
96
- * the CLI (worker-pool's fork path stamps cliId; rememberLastCliInput
97
- * writes lastCliInput on every input). Daemon-command scratches never set
98
- * either, so the predicate survives restart and is robust across paths.
99
- *
100
- * Apply at every relay surface that consumes a candidate `ds`:
101
- * - relay-picker.ts collectRelayPickerEntries (don't list scratches)
102
- * - card-handler.ts relay_confirm preflight (don't M1 + transferSession a scratch)
103
- * - this file's transferSession depth defense (catch any caller that bypassed both upstream guards)
104
- * - command-handler.ts /relay --create leader guard
105
- */
106
- export function isRelayableRealSession(ds) {
107
- if (ds.worker)
108
- return true;
109
- if (ds.session.cliId)
110
- return true;
111
- if (ds.session.lastCliInput)
112
- return true;
113
- return false;
114
- }
115
- // ─── Terminal URL helpers ──────────────────────────────────────────────────
116
- // config.web.externalHost is a live getter (re-resolves the LAN IP each read
117
- // when WEB_EXTERNAL_HOST is unset), so building the URL fresh at every card
118
- // render/patch is enough to keep links pointing at the current network.
119
- function terminalReadUrl(port) {
120
- return `http://${config.web.externalHost}:${port}`;
121
- }
122
- function terminalWriteUrl(port, token) {
123
- return `${terminalReadUrl(port)}?token=${encodeURIComponent(token)}`;
124
- }
125
- // Per-bot opt-out: when true, botmux never posts/patches the live streaming
126
- // session card. Read fresh from the in-memory registry so a dashboard toggle
127
- // takes effect without a daemon restart. The `/card` command can override it
128
- // per-session via `ds.streamingCardForced` (manually summon a live card).
129
- function streamingCardDisabled(ds) {
130
- if (ds.streamingCardForced)
131
- return false;
132
- try {
133
- return getBot(ds.larkAppId).config.disableStreamingCard === true;
134
- }
135
- catch {
136
- return false;
137
- }
138
- }
139
- // Per-bot opt-in: the writable terminal link to embed directly in the streaming
140
- // card body (token included). Returns undefined unless the bot enabled it AND
141
- // the worker port/token are known. Exported for card-handler's re-renders so the
142
- // link stays put across button-driven card updates.
143
- export function writableTerminalLinkFor(ds) {
144
- try {
145
- if (getBot(ds.larkAppId).config.writableTerminalLinkInCard !== true)
146
- return undefined;
147
- }
148
- catch {
149
- return undefined;
150
- }
151
- if (!ds.workerPort || !ds.workerToken)
152
- return undefined;
153
- return terminalWriteUrl(ds.workerPort, ds.workerToken);
154
- }
155
74
  // ─── Helpers ────────────────────────────────────────────────────────────────
156
75
  function tag(ds) {
157
76
  return ds.session.sessionId.substring(0, 8);
@@ -159,36 +78,6 @@ function tag(ds) {
159
78
  function sessionCliId(ds, botCfg) {
160
79
  return ds.session.cliId ?? botCfg.cliId;
161
80
  }
162
- function loadKnownBotOpenIdsForApp(larkAppId) {
163
- const dataDir = config.session.dataDir;
164
- let crossRef = {};
165
- const crossRefPath = join(dataDir, `bot-openids-${larkAppId}.json`);
166
- if (existsSync(crossRefPath)) {
167
- const parsed = JSON.parse(readFileSync(crossRefPath, 'utf-8'));
168
- if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
169
- crossRef = parsed;
170
- }
171
- }
172
- let botEntries = [];
173
- const botInfoPath = join(dataDir, 'bots-info.json');
174
- if (existsSync(botInfoPath)) {
175
- const parsed = JSON.parse(readFileSync(botInfoPath, 'utf-8'));
176
- if (Array.isArray(parsed))
177
- botEntries = parsed;
178
- }
179
- return knownBotOpenIdsFromCrossRef(crossRef, botEntries, larkAppId);
180
- }
181
- function daemonCardFooterRecipientOpenId(ds) {
182
- const owner = ds.session.ownerOpenId;
183
- if (!owner)
184
- return undefined;
185
- try {
186
- return loadKnownBotOpenIdsForApp(ds.larkAppId).has(owner) ? undefined : owner;
187
- }
188
- catch {
189
- return owner;
190
- }
191
- }
192
81
  export function clearUsageLimitState(ds) {
193
82
  if (ds.usageLimitRetryTimer) {
194
83
  clearTimeout(ds.usageLimitRetryTimer);
@@ -208,9 +97,9 @@ function scheduleUsageLimitCardPatch(ds) {
208
97
  return;
209
98
  const bot = getBot(ds.larkAppId);
210
99
  const effectiveCliId = sessionCliId(ds, bot.config);
211
- const readUrl = terminalReadUrl(port);
100
+ const readUrl = `http://${config.web.externalHost}:${port}`;
212
101
  const turnTitle = ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId);
213
- const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent ?? '', 'limited', effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, !!ds.adoptedFrom, false, localeForBot(ds.larkAppId), ds.usageLimit, writableTerminalLinkFor(ds));
102
+ const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent ?? '', 'limited', effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, !!ds.adoptedFrom, false, localeForBot(ds.larkAppId), ds.usageLimit);
214
103
  scheduleCardPatch(ds, cardJson);
215
104
  }
216
105
  function armUsageLimitRetryTimer(ds, previous) {
@@ -343,110 +232,6 @@ export function recallFrozenCards(ds) {
343
232
  }
344
233
  logger.info(`[${tag(ds)}] Recalled ${targets.length} previous streaming card(s)`);
345
234
  }
346
- /**
347
- * Force-post a fresh streaming card for `ds`, bypassing the per-bot
348
- * `disableStreamingCard` opt-out. Backs the `/card` command: a user can
349
- * manually summon a live card in an otherwise-quiet session. Parks the current
350
- * card (if any) first so `recallFrozenCards` withdraws it once the fresh one
351
- * lands — the thread ends up with a single live card. Returns false when the
352
- * worker terminal isn't ready yet (no port), so the caller can surface a
353
- * friendly "not ready" message.
354
- *
355
- * Note: this does NOT itself flip `ds.streamingCardForced` — the caller sets
356
- * that so the card keeps live-patching afterwards even when the bot opted out.
357
- */
358
- export async function postFreshStreamingCard(ds, sessionReply) {
359
- const port = ds.workerPort ?? ds.session.webPort;
360
- if (!port)
361
- return false;
362
- const botCfg = getBot(ds.larkAppId).config;
363
- const effectiveCliId = sessionCliId(ds, botCfg);
364
- const readUrl = terminalReadUrl(port);
365
- const title = ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId);
366
- const status = ds.lastScreenStatus ?? 'idle';
367
- // Park the current card (no-op when there's none) so the fresh one replaces
368
- // rather than duplicates it.
369
- parkStreamCard(ds);
370
- // Snapshot prior identity for rollback on POST failure (restore all three
371
- // together so a failed /card leaves no orphaned nonce/pending state).
372
- const prevCardId = ds.streamCardId;
373
- const prevNonce = ds.streamCardNonce;
374
- const prevPending = ds.streamCardPending;
375
- ds.streamCardNonce = randomBytes(4).toString('hex');
376
- const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, title, ds.lastScreenContent ?? '', status, effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, !!ds.adoptedFrom, false, localeForBot(ds.larkAppId), cardUsageLimit(ds), writableTerminalLinkFor(ds));
377
- ds.streamCardId = CARD_POSTING_SENTINEL;
378
- try {
379
- ds.streamCardId = await sessionReply(sessionAnchorId(ds), cardJson, 'interactive', ds.larkAppId);
380
- // This card is now the live one for the current turn. Clear the new-turn
381
- // pending flag so the next screen_update PATCHes it instead of POSTing a
382
- // duplicate (the gate above only suppresses cards when disabled+unforced;
383
- // /card forces them on, so a stale pending flag would otherwise re-POST).
384
- ds.streamCardPending = false;
385
- persistStreamCardState(ds);
386
- recallFrozenCards(ds);
387
- logger.info(`[${tag(ds)}] Posted streaming card via /card`);
388
- return true;
389
- }
390
- catch (err) {
391
- ds.streamCardId = prevCardId;
392
- ds.streamCardNonce = prevNonce;
393
- ds.streamCardPending = prevPending;
394
- logger.warn(`[${tag(ds)}] /card POST failed: ${err}`);
395
- return false;
396
- }
397
- }
398
- /**
399
- * Audience for a private `/card`: the bot's `allowedUsers` (the canOperate set —
400
- * owner & co-owners), deduped, `ou_` only. Talk-only grants (`globalGrants` /
401
- * `chatGrants`) and a bare triggerer are intentionally NOT included: the private
402
- * card is owner-only. A grant-authorized user who runs `/card` therefore does
403
- * not receive a card (matches the "授权人不发" rule). Empty when the bot has no
404
- * `allowedUsers` (fully-open mode → no owner to send to).
405
- */
406
- export function resolvePrivateCardAudience(ds) {
407
- const bot = getBot(ds.larkAppId);
408
- const set = new Set();
409
- for (const u of bot.resolvedAllowedUsers)
410
- if (u.startsWith('ou_'))
411
- set.add(u);
412
- return [...set];
413
- }
414
- /**
415
- * Private `/card`: build a one-shot snapshot of the current terminal and send it
416
- * as an ephemeral (visible-to-one) card to each open_id in `audience`, one API
417
- * call each (concurrency-capped). Never posts a group-visible card and never
418
- * patches — privacy is the whole point, so there is deliberately no fallback.
419
- * Returns per-recipient counts so the caller can report progress without leaking
420
- * the audience list into the chat.
421
- */
422
- export async function postPrivateSnapshotCard(ds, audience) {
423
- const port = ds.workerPort ?? ds.session.webPort;
424
- if (!port)
425
- return { sent: 0, total: audience.length, notReady: true };
426
- const botCfg = getBot(ds.larkAppId).config;
427
- const effectiveCliId = sessionCliId(ds, botCfg);
428
- const readUrl = terminalReadUrl(port);
429
- const title = ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId);
430
- const status = ds.lastScreenStatus ?? 'idle';
431
- const cardJson = buildPrivateSnapshotCard(readUrl, title, status, effectiveCliId, ds.currentImageKey, ds.lastScreenContent ?? '', ds.session.sessionId, sessionAnchorId(ds), localeForBot(ds.larkAppId), cardUsageLimit(ds));
432
- let sent = 0;
433
- // Cap concurrency: Feishu per-chat ~40 QPS, ephemeral total 50/s.
434
- const CONCURRENCY = 5;
435
- for (let i = 0; i < audience.length; i += CONCURRENCY) {
436
- const batch = audience.slice(i, i + CONCURRENCY);
437
- await Promise.all(batch.map(async (openId) => {
438
- try {
439
- await sendEphemeralCard(ds.larkAppId, ds.chatId, openId, cardJson);
440
- sent++;
441
- }
442
- catch (err) {
443
- logger.warn(`[${tag(ds)}] private /card ephemeral send to ${openId.substring(0, 8)}… failed: ${err}`);
444
- }
445
- }));
446
- }
447
- logger.info(`[${tag(ds)}] private /card: ephemeral sent ${sent}/${audience.length}`);
448
- return { sent, total: audience.length, notReady: false };
449
- }
450
235
  // ─── Card PATCH serialization queue ─────────────────────────────────────────
451
236
  // Only one PATCH in-flight at a time per session. New PATCHes queue on
452
237
  // ds.pendingCardJson (latest wins). When the in-flight PATCH completes,
@@ -459,9 +244,6 @@ export async function postPrivateSnapshotCard(ds, audience) {
459
244
  * any previously queued value — only the latest state matters).
460
245
  */
461
246
  export function scheduleCardPatch(ds, cardJson) {
462
- // Bot opted out of the streaming card — never patch one into existence.
463
- if (streamingCardDisabled(ds))
464
- return;
465
247
  ds.pendingCardJson = cardJson;
466
248
  // Capture the card ID now — by the time flushCardPatch runs, ds.streamCardId
467
249
  // may have been overwritten by a new turn's card (CARD_POSTING_SENTINEL).
@@ -523,32 +305,7 @@ export function ensureCliSkills(cliId, cliPathOverride) {
523
305
  if (skillsInstalledCliIds.has(cliId))
524
306
  return;
525
307
  const adapter = createCliAdapterSync(cliId, cliPathOverride);
526
- if (adapter.pluginDir) {
527
- // 动态注入:skill 写进插件目录,spawn 时用 --plugin-dir 注入,仅本次会话可见。
528
- // 不再写全局 skillsDir。(全局 ~/.claude/skills 的历史残留清理改由
529
- // cleanupGlobalBotmuxSkillsOnce 在启动时独立于 cliId 执行。)
530
- ensurePluginSkills(cliId, adapter.pluginDir);
531
- }
532
- else {
533
- ensureSkills(cliId, adapter.skillsDir);
534
- }
535
- // askUserQuestion 接管策略:hook 优先 + 非 hook CLI 用 skill 兜底。
536
- // - asksViaHook=true(Claude/OpenCode):通过 hook 拦截原生 AskUserQuestion,删掉
537
- // botmux-ask skill,避免 skill 与 hook 双重弹卡。
538
- // Claude 走 --settings 进程级注入;OpenCode 走 hookInstall 插件写文件。
539
- // - asksViaHook 未设(Codex/Cursor/尚未接 hook 的终端原生 CLI):保留 botmux-ask
540
- // skill 作兜底,让 agent 仍可用 `botmux ask` 把选择题引到飞书。
541
- if (adapter.hookInstall) {
542
- try {
543
- installHook(cliId, adapter.hookInstall, hookCommandFor(cliId));
544
- }
545
- catch (err) {
546
- logger.warn(`[hook] install failed for ${cliId}: ${err instanceof Error ? err.message : String(err)}`);
547
- }
548
- }
549
- // botmux-ask 落在与其它 skill 同一目录:plugin 模式下是 {pluginDir}/skills。
550
- const askSkillsDir = adapter.pluginDir ? join(adapter.pluginDir, 'skills') : adapter.skillsDir;
551
- ensureAskSkill(cliId, askSkillsDir, !adapter.asksViaHook);
308
+ ensureSkills(cliId, adapter.skillsDir);
552
309
  skillsInstalledCliIds.add(cliId);
553
310
  }
554
311
  // ─── Legacy MCP config cleanup ──────────────────────────────────────────────
@@ -624,11 +381,9 @@ export function cleanupLegacyMcpConfig(cliId) {
624
381
  }
625
382
  break;
626
383
  }
627
- case 'opencode':
628
- case 'mtr': {
629
- // ~/.config/opencode/{opencode,mtr}.json → { mcp: { botmux } } or { mcpServers: { botmux } }
630
- const file = cliId === 'mtr' ? 'mtr.json' : 'opencode.json';
631
- const p = join(home, '.config', 'opencode', file);
384
+ case 'opencode': {
385
+ // ~/.config/opencode/opencode.json → { mcp: { botmux } } or { mcpServers: { botmux } }
386
+ const p = join(home, '.config', 'opencode', 'opencode.json');
632
387
  const removed = removeJsonKey(p, ['mcp'], 'botmux') ||
633
388
  removeJsonKey(p, ['mcpServers'], 'botmux') ||
634
389
  removeJsonKey(p, ['mcp', 'servers'], 'botmux');
@@ -658,23 +413,9 @@ export function cleanupLegacyMcpConfig(cliId) {
658
413
  * Both steps are idempotent and best-effort.
659
414
  */
660
415
  export function ensureCliEnv(cliId, cliPathOverride) {
661
- cleanupGlobalBotmuxSkillsOnce();
662
416
  ensureCliSkills(cliId, cliPathOverride);
663
417
  cleanupLegacyMcpConfig(cliId);
664
418
  }
665
- let globalBotmuxSkillsCleaned = false;
666
- /** One-time, CLI-independent cleanup of botmux skills that older versions
667
- * installed into the global `~/.claude/skills`. Claude now injects skills via
668
- * `--plugin-dir`, so any leftover `botmux-*` there leaks into the user's
669
- * standalone `claude` regardless of which CLI THIS daemon's bot uses — so the
670
- * cleanup must NOT be gated on `adapter.pluginDir` (which only fires for a
671
- * Claude bot). Runs at daemon startup via ensureCliEnv. */
672
- function cleanupGlobalBotmuxSkillsOnce() {
673
- if (globalBotmuxSkillsCleaned)
674
- return;
675
- globalBotmuxSkillsCleaned = true;
676
- removeGlobalBotmuxSkills('~/.claude/skills');
677
- }
678
419
  // ─── Claude Code folder-trust pre-acceptance ─────────────────────────────────
679
420
  //
680
421
  // A freshly spawned `claude` in a workingDir that has never been trusted blocks
@@ -808,234 +549,6 @@ export async function closeSession(sessionId) {
808
549
  const alreadyClosed = !killedLive && !wasOpen;
809
550
  return { ok: true, alreadyClosed };
810
551
  }
811
- /**
812
- * Set an entry on an active-sessions Map, but if the key is already occupied
813
- * by a DIFFERENT DaemonSession, close that occupant first. Replaces bare
814
- * `activeSessions.set(key, ds)` at sites where a silent overwrite would leak
815
- * the prior entry's worker + leave its store row stuck in `status='active'`.
816
- *
817
- * The Map is passed explicitly so callers operate on the same instance they
818
- * already hold (restoreActiveSessions takes the daemon's Map as a parameter;
819
- * transferSession reaches it through `activeSessionsRegistry`). In production
820
- * both refer to the same object — the daemon registers its Map at boot — but
821
- * decoupling avoids module-state assumptions in tests.
822
- *
823
- * Canonical collision case: restoreActiveSessions at daemon boot iterating
824
- * two on-disk active sessions that resolve to the same chat-scope key (e.g.
825
- * a /relay command's scratch session + the real session that was transferred
826
- * into the same chat by a prior daemon run). Without this helper the later
827
- * iterated entry silently wins, the earlier one becomes a ghost-active.
828
- *
829
- * Setting the same `ds` at its own key is a no-op (no close).
830
- */
831
- export async function setActiveSessionSafe(map, key, ds) {
832
- const prev = map.get(key);
833
- if (prev && prev !== ds) {
834
- logger.warn(`[setActiveSessionSafe] key already occupied by ${prev.session.sessionId.substring(0, 8)} ` +
835
- `(worker=${prev.worker ? 'live' : 'null'}); closing it before set`);
836
- await closeSession(prev.session.sessionId);
837
- }
838
- map.set(key, ds);
839
- }
840
- // ─── Session transfer (cross-chat relay) ────────────────────────────────────
841
- /**
842
- * Transfer an active session from its current chat to a new chat. The CLI
843
- * process keeps running inside its tmux session — only the routing fields
844
- * (chatId, rootMessageId, scope) and activeSessions key are rewritten. After
845
- * the rewrite, forkWorker spawns a new worker that re-attaches to the same
846
- * `bmx-<sessionId>` tmux, so the AI's transcript continues without break.
847
- *
848
- * Visible side effects:
849
- * - Lark messages in the *source* chat remain where they were — we have no
850
- * API to move them. Only the worker's *routing* moves; the AI's memory
851
- * follows via the CLI's persistent jsonl on disk.
852
- * - Cards posted by the prior worker stay in the source chat. We clear
853
- * streamCardId/Nonce/imageKey so the new worker posts fresh cards in the
854
- * target chat instead of trying to PATCH unreachable old ones.
855
- *
856
- * Pre-conditions (entry guards, all checked synchronously up-front — no
857
- * idle-wait loop; busy workers are refused immediately so the caller can
858
- * report a deterministic outcome and the user retries when the worker
859
- * quiets):
860
- * - Session must be currently active (live worker + activeSessions entry)
861
- * - Source must not be a pendingRepo placeholder (no CLI ever started)
862
- * - Source must not be an adopted external-tmux session
863
- * - Source worker must be in idle/limited (or already dead) — otherwise
864
- * refuse with `worker_busy`
865
- * - Target chat must not already host a real chat-scope session for the
866
- * same bot (`target_chat_has_session`). Scratch (worker:null) occupants
867
- * are NOT a conflict — they're command-time placeholders and we close
868
- * them in-line to free the slot before continuing.
869
- *
870
- * Idempotent for `same_chat`: returns error without side effects when the
871
- * source chat equals the target chat.
872
- */
873
- export async function transferSession(sessionId, targetChatId, targetRootMessageId,
874
- /**
875
- * Target chat type — narrowed to `'group'` at the type level. The picker-
876
- * mode entry guard in command-handler.ts refuses p2p and topic chats
877
- * upfront; `/relay --create` builds the target by createGroupWithBots so
878
- * it's a regular group by construction; the cross-daemon migrate-to-chat
879
- * IPC inherits the same target. Every call site can vouch — TS prevents
880
- * any non-'group' literal from reaching here, and the runtime check just
881
- * below catches mock data / future bypasses.
882
- */
883
- targetChatType, opts) {
884
- // Depth defense — unreachable per TS narrowing above, but guards against
885
- // raw-string casting at module boundaries (mocks, HTTP body parses, etc.).
886
- if (targetChatType !== 'group') {
887
- return { ok: false, error: 'target_chat_type_unsupported' };
888
- }
889
- const ds = findActiveBySessionId(sessionId);
890
- if (!ds)
891
- return { ok: false, error: 'session_not_active' };
892
- if (targetChatId === ds.chatId)
893
- return { ok: false, error: 'same_chat' };
894
- // pendingRepo: the user created a session via M0 but hasn't picked a repo
895
- // yet, so worker is null and the CLI has never run. Relaying produces an
896
- // empty new-chat session with no AI memory — refuse so the user finishes
897
- // setup in the original chat first.
898
- if (ds.pendingRepo)
899
- return { ok: false, error: 'not_started_yet' };
900
- // Depth defense: daemon-command scratch (worker:null + no persisted CLI
901
- // markers) must not be migrated. Upstream paths (picker filter, card-
902
- // handler confirm preflight, /relay --create leader guard) should already
903
- // refuse these — this catches any caller that bypassed all three (e.g.
904
- // a future code path, a direct dashboard IPC, a test reaching in
905
- // manually). Using `isRelayableRealSession` instead of `ds.hasHistory`
906
- // makes the predicate survive restoreActiveSessions which currently sets
907
- // hasHistory:true unconditionally (session-manager.ts:618).
908
- if (!isRelayableRealSession(ds))
909
- return { ok: false, error: 'not_started_yet' };
910
- // Adopt sessions wrap a CLI process that botmux didn't spawn — the user
911
- // owns it inside their own tmux pane, so moving routing here would be
912
- // surprising and we don't control the tmux session's lifecycle. Refuse.
913
- if (ds.session.adoptedFrom)
914
- return { ok: false, error: 'adopt_not_relayable' };
915
- // Busy worker: refuse immediately rather than waiting. An idle-wait loop
916
- // (previously 60s) created an asymmetry with the peer-dispatch HTTP
917
- // timeout (5s) — peer's transferSession was still polling while the
918
- // leader had already abort+report 'busy', producing reports that
919
- // disagreed with reality. Cleaner contract: refuse on first miss, let
920
- // the user retry when the turn settles.
921
- const st = ds.lastScreenStatus;
922
- if (ds.worker && !ds.worker.killed && st !== 'idle' && st !== 'limited') {
923
- return { ok: false, error: 'worker_busy' };
924
- }
925
- // Existing-session guard: a chat-scope session at the target chatId would
926
- // collide on sessionKey(targetChatId, larkAppId) after the rewrite, and
927
- // Map.set would silently orphan the prior entry's worker. We split the
928
- // collision predicate two ways:
929
- // - real session (worker !== null): refuse the transfer
930
- // - scratch session (worker === null): a daemon-command placeholder
931
- // (e.g. the /relay command itself created one when typed in this
932
- // chat); the slot is logically free, but the placeholder lingers in
933
- // the store with status='active'. Collect and close it so the post-
934
- // transfer Map.set doesn't silently overwrite it (which leaves the
935
- // scratch as a ghost-active on next daemon restart — exact bug we're
936
- // fixing).
937
- // We only check chat-scope entries — thread-scope sessions in the same
938
- // chat are keyed by rootMessageId, so they don't collide.
939
- const scratchesToClose = [];
940
- if (activeSessionsRegistry) {
941
- for (const existing of activeSessionsRegistry.values()) {
942
- if (existing === ds)
943
- continue;
944
- if (existing.larkAppId !== ds.larkAppId)
945
- continue;
946
- if (existing.chatId !== targetChatId)
947
- continue;
948
- if (existing.scope !== 'chat')
949
- continue;
950
- if (!existing.worker) {
951
- scratchesToClose.push(existing.session.sessionId);
952
- continue;
953
- }
954
- return { ok: false, error: 'target_chat_has_session' };
955
- }
956
- }
957
- for (const sid of scratchesToClose) {
958
- await closeSession(sid);
959
- }
960
- const fkw = opts?.forkWorkerImpl ?? forkWorker;
961
- const kw = opts?.killWorkerImpl ?? killWorker;
962
- const tagPrefix = sessionId.substring(0, 8);
963
- const oldAnchor = sessionAnchorId(ds);
964
- const oldChatId = ds.chatId;
965
- // Freeze the source-chat streaming card BEFORE we kill the worker (and
966
- // before we clear streamCardId below). The live card's action buttons
967
- // (close / toggle / get write link) carry `session_id` in their value, so
968
- // clicks AFTER relay still reach the now-relocated session — closing it,
969
- // toggling its display mode, etc. — with feedback landing on the NEW
970
- // card in the target chat. PATCH the source-chat card to an inert
971
- // snapshot so the user sees clearly it's historical, and so the buttons
972
- // are gone. Best-effort: on PATCH failure (card withdrawn, expired) we
973
- // log and continue; the relay itself must not depend on this.
974
- if (ds.streamCardId && ds.streamCardId !== CARD_POSTING_SENTINEL) {
975
- try {
976
- const cliId = ds.session.cliId
977
- ?? (() => { try {
978
- return getBot(ds.larkAppId).config.cliId;
979
- }
980
- catch {
981
- return undefined;
982
- } })();
983
- const frozenJson = buildRelayedFrozenCard(ds.currentTurnTitle || ds.session.title || '', cliId, ds.currentImageKey, localeForBot(ds.larkAppId));
984
- await updateMessage(ds.larkAppId, ds.streamCardId, frozenJson);
985
- }
986
- catch (err) {
987
- logger.warn(`[${tagPrefix}] freeze source-chat card failed: ${err instanceof Error ? err.message : err}`);
988
- }
989
- }
990
- // Detach worker — TmuxBackend.kill() does NOT destroy the tmux session, so
991
- // the CLI process and its rolling jsonl continue running.
992
- kw(ds);
993
- activeSessionsRegistry?.delete(sessionKey(oldAnchor, ds.larkAppId));
994
- // Rewrite routing fields. Target chat is always chat-scope: leader posts a
995
- // notification message (M1) used as `targetRootMessageId` for trace, but
996
- // chat-scope routes by chatId anyway, so M1 is purely audit/UX.
997
- ds.session.chatId = targetChatId;
998
- ds.session.rootMessageId = targetRootMessageId;
999
- ds.session.scope = 'chat';
1000
- ds.session.chatType = targetChatType;
1001
- ds.session.lastMessageAt = new Date().toISOString();
1002
- // Card state was pinned to the source chat — clear so the new worker posts
1003
- // a fresh card in the target chat instead of trying to PATCH a message that
1004
- // lives in another chat entirely (the source card was just frozen above).
1005
- ds.session.streamCardId = undefined;
1006
- ds.session.streamCardNonce = undefined;
1007
- ds.session.currentImageKey = undefined;
1008
- // Mirror onto runtime DaemonSession.
1009
- ds.chatId = targetChatId;
1010
- ds.chatType = targetChatType;
1011
- ds.scope = 'chat';
1012
- ds.streamCardId = undefined;
1013
- ds.streamCardNonce = undefined;
1014
- ds.currentImageKey = undefined;
1015
- sessionStore.updateSession(ds.session);
1016
- const newAnchor = sessionAnchorId(ds);
1017
- if (activeSessionsRegistry) {
1018
- await setActiveSessionSafe(activeSessionsRegistry, sessionKey(newAnchor, ds.larkAppId), ds);
1019
- }
1020
- dashboardEventBus.publish({
1021
- type: 'session.update',
1022
- body: {
1023
- sessionId,
1024
- patch: {
1025
- chatId: targetChatId,
1026
- rootMessageId: targetRootMessageId,
1027
- scope: 'chat',
1028
- chatType: targetChatType,
1029
- },
1030
- },
1031
- });
1032
- // forkWorker with resume=true — TmuxBackend.spawn detects the surviving
1033
- // `bmx-<sessionId>` session and re-attaches instead of creating a new one.
1034
- fkw(ds, '', /*resume*/ true);
1035
- logger.info(`[${tagPrefix}] transferred ${oldChatId} → ${targetChatId} ` +
1036
- `(anchor ${oldAnchor.substring(0, 8)} → ${newAnchor.substring(0, 8)})`);
1037
- return { ok: true };
1038
- }
1039
552
  // ─── Fork worker ────────────────────────────────────────────────────────────
1040
553
  export function forkWorker(ds, prompt, resume = false) {
1041
554
  const cb = requireCallbacks();
@@ -1109,7 +622,6 @@ export function forkWorker(ds, prompt, resume = false) {
1109
622
  workingDir: cwd,
1110
623
  cliId: botCfg.cliId,
1111
624
  cliPathOverride: botCfg.cliPathOverride,
1112
- model: botCfg.model,
1113
625
  backendType: botCfg.backendType ?? config.daemon.backendType,
1114
626
  prompt,
1115
627
  resume,
@@ -1168,8 +680,8 @@ function setupWorkerHandlers(ds, worker) {
1168
680
  // Persist port so it can be reused after daemon restart
1169
681
  ds.session.webPort = msg.port;
1170
682
  sessionStore.updateSession(ds.session);
1171
- const readOnlyUrl = terminalReadUrl(msg.port);
1172
- const writeUrl = terminalWriteUrl(msg.port, msg.token);
683
+ const readOnlyUrl = `http://${config.web.externalHost}:${msg.port}`;
684
+ const writeUrl = `${readOnlyUrl}?token=${msg.token}`;
1173
685
  logger.info(`[${t}] Worker ready, terminal at ${readOnlyUrl}`);
1174
686
  if (ds.usageLimit) {
1175
687
  ds.lastScreenStatus = 'limited';
@@ -1183,14 +695,6 @@ function setupWorkerHandlers(ds, worker) {
1183
695
  patch: { webPort: msg.port },
1184
696
  },
1185
697
  });
1186
- // Bot opted out of the streaming card: the terminal is up and the
1187
- // final answer will still arrive via `botmux send`; just don't post the
1188
- // live status card. (workerPort/token above are still set so the web
1189
- // terminal + dashboard keep working.)
1190
- if (streamingCardDisabled(ds)) {
1191
- logger.info(`[${t}] Streaming card disabled for this bot — skipping card post`);
1192
- break;
1193
- }
1194
698
  // If a previous streaming card survived (e.g. daemon restart), try to
1195
699
  // PATCH it with the new "starting" state instead of POSTing a fresh card.
1196
700
  // ds.streamCardPending forces a new card (e.g. mid-session repo switch
@@ -1204,14 +708,8 @@ function setupWorkerHandlers(ds, worker) {
1204
708
  // Reuse persisted nonce so existing card buttons (toggle/etc) keep working.
1205
709
  if (!ds.streamCardNonce)
1206
710
  ds.streamCardNonce = randomBytes(4).toString('hex');
1207
- // Prefer the last-known screen status when we have one — for /relay
1208
- // resume the worker was idle/limited at transfer time and the
1209
- // CLI didn't actually stop, so showing "starting" right after
1210
- // the M1 "已接力" announcement is misleading. Fresh-spawn worker
1211
- // and post-daemon-restart paths still see lastScreenStatus
1212
- // undefined and fall back to 'starting' (unchanged behavior).
1213
- const initStatus = ds.usageLimit ? 'limited' : (ds.lastScreenStatus ?? 'starting');
1214
- const streamCardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readOnlyUrl, initTitle, ds.lastScreenContent ?? '', initStatus, effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, isAdopt, showTakeover, loc, initStatus === 'limited' ? ds.usageLimit : undefined, writableTerminalLinkFor(ds));
711
+ const initStatus = ds.usageLimit ? 'limited' : 'starting';
712
+ const streamCardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readOnlyUrl, initTitle, ds.lastScreenContent ?? '', initStatus, effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, isAdopt, showTakeover, loc, initStatus === 'limited' ? ds.usageLimit : undefined);
1215
713
  await updateMessage(ds.larkAppId, restoredCardId, streamCardJson);
1216
714
  persistStreamCardState(ds);
1217
715
  // Re-sync worker's display mode (it starts fresh in 'hidden')
@@ -1239,16 +737,8 @@ function setupWorkerHandlers(ds, worker) {
1239
737
  try {
1240
738
  ds.streamCardNonce = randomBytes(4).toString('hex');
1241
739
  const initTitle = ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId);
1242
- // See PATCH-branch comment above re: lastScreenStatus preference.
1243
- // For relay (kill+fork with surviving tmux/CLI), this avoids the
1244
- // jarring "启动中" right after the M1 "已接力" announcement.
1245
- const initStatus = ds.usageLimit ? 'limited' : (ds.lastScreenStatus ?? 'starting');
1246
- const streamCardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readOnlyUrl, initTitle,
1247
- // For /relay resume, ds.lastScreenContent is the cached pane
1248
- // from before the kill+fork — using it avoids a blank flash
1249
- // before the first screen_update lands. Fresh worker spawn
1250
- // has lastScreenContent undefined → '' (unchanged).
1251
- ds.lastScreenContent ?? '', initStatus, effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, isAdopt, showTakeover, loc, initStatus === 'limited' ? ds.usageLimit : undefined, writableTerminalLinkFor(ds));
740
+ const initStatus = ds.usageLimit ? 'limited' : 'starting';
741
+ const streamCardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readOnlyUrl, initTitle, '', initStatus, effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, isAdopt, showTakeover, loc, initStatus === 'limited' ? ds.usageLimit : undefined);
1252
742
  ds.streamCardId = await cb.sessionReply(sessionAnchorId(ds), streamCardJson, 'interactive', ds.larkAppId);
1253
743
  persistStreamCardState(ds);
1254
744
  // Re-sync worker's display mode (it starts fresh in 'hidden')
@@ -1321,10 +811,6 @@ function setupWorkerHandlers(ds, worker) {
1321
811
  },
1322
812
  });
1323
813
  }
1324
- // Bot opted out of the streaming card — dashboard SSE above already got
1325
- // the status patch; just don't touch any Lark card.
1326
- if (streamingCardDisabled(ds))
1327
- break;
1328
814
  const readUrl = `http://${config.web.externalHost}:${ds.workerPort}`;
1329
815
  const turnTitle = ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId);
1330
816
  const mode = ds.displayMode ?? 'hidden';
@@ -1340,7 +826,7 @@ function setupWorkerHandlers(ds, worker) {
1340
826
  // New turn → image_key from previous turn no longer valid
1341
827
  if (isNewTurn)
1342
828
  ds.currentImageKey = undefined;
1343
- const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, isNewTurn ? '' : msg.content, ds.lastScreenStatus, effectiveCliId, mode, ds.streamCardNonce, ds.currentImageKey, isAdopt, showTakeover, loc, cardUsageLimit(ds), writableTerminalLinkFor(ds));
829
+ const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, isNewTurn ? '' : msg.content, ds.lastScreenStatus, effectiveCliId, mode, ds.streamCardNonce, ds.currentImageKey, isAdopt, showTakeover, loc, cardUsageLimit(ds));
1344
830
  // Mark POST in-flight so subsequent screen_updates are dropped,
1345
831
  // not POSTed as duplicate cards.
1346
832
  ds.streamCardPending = false;
@@ -1374,7 +860,7 @@ function setupWorkerHandlers(ds, worker) {
1374
860
  const statusChanged = prevStatus !== ds.lastScreenStatus;
1375
861
  if (!statusChanged)
1376
862
  break;
1377
- const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, msg.content, ds.lastScreenStatus, effectiveCliId, mode, ds.streamCardNonce, ds.currentImageKey, isAdopt, showTakeover, loc, cardUsageLimit(ds), writableTerminalLinkFor(ds));
863
+ const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, msg.content, ds.lastScreenStatus, effectiveCliId, mode, ds.streamCardNonce, ds.currentImageKey, isAdopt, showTakeover, loc, cardUsageLimit(ds));
1378
864
  scheduleCardPatch(ds, cardJson);
1379
865
  }
1380
866
  break;
@@ -1394,7 +880,7 @@ function setupWorkerHandlers(ds, worker) {
1394
880
  break;
1395
881
  const readUrl = `http://${config.web.externalHost}:${ds.workerPort}`;
1396
882
  const turnTitle = ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId);
1397
- const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent ?? '', ds.lastScreenStatus, effectiveCliId, 'screenshot', ds.streamCardNonce, ds.currentImageKey, isAdopt, showTakeover, loc, cardUsageLimit(ds), writableTerminalLinkFor(ds));
883
+ const cardJson = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent ?? '', ds.lastScreenStatus, effectiveCliId, 'screenshot', ds.streamCardNonce, ds.currentImageKey, isAdopt, showTakeover, loc, cardUsageLimit(ds));
1398
884
  scheduleCardPatch(ds, cardJson);
1399
885
  break;
1400
886
  }
@@ -1451,7 +937,7 @@ function setupWorkerHandlers(ds, worker) {
1451
937
  if (ds.streamCardId && ds.workerPort) {
1452
938
  const readUrl = `http://${config.web.externalHost}:${ds.workerPort}`;
1453
939
  const turnTitle = ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId);
1454
- const frozenCard = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent ?? '', 'idle', effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, isAdopt, showTakeover, loc, undefined, writableTerminalLinkFor(ds));
940
+ const frozenCard = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent ?? '', 'idle', effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, isAdopt, showTakeover);
1455
941
  scheduleCardPatch(ds, frozenCard);
1456
942
  }
1457
943
  killWorker(ds);
@@ -1483,7 +969,7 @@ function setupWorkerHandlers(ds, worker) {
1483
969
  if (ds.streamCardId && ds.workerPort) {
1484
970
  const readUrl = `http://${config.web.externalHost}:${ds.workerPort}`;
1485
971
  const turnTitle = ds.currentTurnTitle || ds.session.title || getCliDisplayName(effectiveCliId);
1486
- const frozenCard = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent ?? '', 'idle', effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, isAdopt, showTakeover, loc, undefined, writableTerminalLinkFor(ds));
972
+ const frozenCard = buildStreamingCard(ds.session.sessionId, sessionAnchorId(ds), readUrl, turnTitle, ds.lastScreenContent ?? '', 'idle', effectiveCliId, ds.displayMode ?? 'hidden', ds.streamCardNonce, ds.currentImageKey, isAdopt, showTakeover, loc);
1487
973
  scheduleCardPatch(ds, frozenCard);
1488
974
  }
1489
975
  // Kill the worker process to free resources
@@ -1551,14 +1037,12 @@ function setupWorkerHandlers(ds, worker) {
1551
1037
  }
1552
1038
  if (!msg.userText.trim() && !msg.assistantText.trim())
1553
1039
  break;
1554
- const recipientOpenId = daemonCardFooterRecipientOpenId(ds);
1555
1040
  const cardJson = buildContextualReplyCard({
1556
1041
  title: '📜 /adopt 前最后一轮',
1557
1042
  userText: msg.userText,
1558
1043
  assistantText: msg.assistantText,
1559
1044
  assistantLabel: getCliDisplayName(effectiveCliId),
1560
- recipientOpenId,
1561
- brand: resolveBrandLabel(ds.larkAppId),
1045
+ recipientOpenId: ds.session.ownerOpenId,
1562
1046
  });
1563
1047
  cb.sessionReply(sessionAnchorId(ds), cardJson, 'interactive', ds.larkAppId).catch((err) => {
1564
1048
  logger.warn(`[${t}] Failed to deliver adopt_preamble to Lark: ${err.message}`);
@@ -1620,7 +1104,6 @@ function deliverFinalOutput(ds, msg, t, attempt) {
1620
1104
  // they use the contextual card so the user prompt sits in a
1621
1105
  // blockquote and only the assistant body goes through full markdown
1622
1106
  // rendering.
1623
- const recipientOpenId = daemonCardFooterRecipientOpenId(ds);
1624
1107
  const cardJson = msg.kind === 'local-turn' || msg.kind === 'local-turn-headless'
1625
1108
  ? buildContextualReplyCard({
1626
1109
  title: msg.kind === 'local-turn-headless'
@@ -1629,10 +1112,9 @@ function deliverFinalOutput(ds, msg, t, attempt) {
1629
1112
  userText: msg.kind === 'local-turn' ? msg.userText ?? '' : undefined,
1630
1113
  assistantText: msg.content,
1631
1114
  assistantLabel: getCliDisplayName(effectiveCliId),
1632
- recipientOpenId,
1633
- brand: resolveBrandLabel(ds.larkAppId),
1115
+ recipientOpenId: ds.session.ownerOpenId,
1634
1116
  })
1635
- : buildMarkdownCard(msg.content, recipientOpenId, resolveBrandLabel(ds.larkAppId));
1117
+ : buildMarkdownCard(msg.content, ds.session.ownerOpenId);
1636
1118
  await cb.sessionReply(sessionAnchorId(ds), cardJson, 'interactive', ds.larkAppId);
1637
1119
  ds.lastBridgeEmittedUuid = msg.lastUuid;
1638
1120
  logger.info(`[${t}] Bridge final_output forwarded (turn ${msg.turnId.substring(0, 8)}, ${msg.content.length} chars, kind=${msg.kind ?? 'bridge'}, attempt ${attempt + 1})`);
@@ -1737,7 +1219,6 @@ export function forkAdoptWorker(ds, opts) {
1737
1219
  workingDir: adopted.cwd,
1738
1220
  cliId: adoptedCliId,
1739
1221
  cliSessionId: isStructuredBridge ? adopted.sessionId : undefined,
1740
- model: botCfg.model,
1741
1222
  backendType: 'tmux',
1742
1223
  prompt: '',
1743
1224
  resume: false,