botmux 3.7.1 → 3.8.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 (622) hide show
  1. package/dist/.runtime-build-id +1 -1
  2. package/dist/adapters/backend/capabilities.d.ts +17 -0
  3. package/dist/adapters/backend/capabilities.d.ts.map +1 -0
  4. package/dist/adapters/backend/capabilities.js +22 -0
  5. package/dist/adapters/backend/capabilities.js.map +1 -0
  6. package/dist/adapters/backend/critical-control-key.d.ts +19 -0
  7. package/dist/adapters/backend/critical-control-key.d.ts.map +1 -0
  8. package/dist/adapters/backend/critical-control-key.js +34 -0
  9. package/dist/adapters/backend/critical-control-key.js.map +1 -0
  10. package/dist/adapters/backend/herdr-backend.d.ts +9 -0
  11. package/dist/adapters/backend/herdr-backend.d.ts.map +1 -1
  12. package/dist/adapters/backend/herdr-backend.js +23 -4
  13. package/dist/adapters/backend/herdr-backend.js.map +1 -1
  14. package/dist/adapters/backend/riff-backend.d.ts.map +1 -1
  15. package/dist/adapters/backend/riff-backend.js +3 -2
  16. package/dist/adapters/backend/riff-backend.js.map +1 -1
  17. package/dist/adapters/backend/session-backend-selector.d.ts +61 -6
  18. package/dist/adapters/backend/session-backend-selector.d.ts.map +1 -1
  19. package/dist/adapters/backend/session-backend-selector.js +173 -9
  20. package/dist/adapters/backend/session-backend-selector.js.map +1 -1
  21. package/dist/adapters/backend/tmux-backend.d.ts +10 -3
  22. package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
  23. package/dist/adapters/backend/tmux-backend.js +25 -8
  24. package/dist/adapters/backend/tmux-backend.js.map +1 -1
  25. package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -1
  26. package/dist/adapters/backend/tmux-pipe-backend.js +3 -2
  27. package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
  28. package/dist/adapters/backend/types.d.ts +45 -4
  29. package/dist/adapters/backend/types.d.ts.map +1 -1
  30. package/dist/adapters/backend/types.js +11 -0
  31. package/dist/adapters/backend/types.js.map +1 -1
  32. package/dist/adapters/backend/zellij-backend.d.ts.map +1 -1
  33. package/dist/adapters/backend/zellij-backend.js +3 -2
  34. package/dist/adapters/backend/zellij-backend.js.map +1 -1
  35. package/dist/adapters/backend/zmx-backend.d.ts +286 -0
  36. package/dist/adapters/backend/zmx-backend.d.ts.map +1 -0
  37. package/dist/adapters/backend/zmx-backend.js +2396 -0
  38. package/dist/adapters/backend/zmx-backend.js.map +1 -0
  39. package/dist/adapters/cli/claude-code.d.ts.map +1 -1
  40. package/dist/adapters/cli/claude-code.js +3 -0
  41. package/dist/adapters/cli/claude-code.js.map +1 -1
  42. package/dist/adapters/cli/codex-app.d.ts.map +1 -1
  43. package/dist/adapters/cli/codex-app.js +5 -1
  44. package/dist/adapters/cli/codex-app.js.map +1 -1
  45. package/dist/adapters/cli/codex.d.ts.map +1 -1
  46. package/dist/adapters/cli/codex.js +26 -1
  47. package/dist/adapters/cli/codex.js.map +1 -1
  48. package/dist/adapters/cli/fs-policy.d.ts +79 -1
  49. package/dist/adapters/cli/fs-policy.d.ts.map +1 -1
  50. package/dist/adapters/cli/fs-policy.js +290 -13
  51. package/dist/adapters/cli/fs-policy.js.map +1 -1
  52. package/dist/adapters/cli/grok.d.ts.map +1 -1
  53. package/dist/adapters/cli/grok.js +4 -1
  54. package/dist/adapters/cli/grok.js.map +1 -1
  55. package/dist/adapters/cli/oh-my-pi.d.ts.map +1 -1
  56. package/dist/adapters/cli/oh-my-pi.js +5 -3
  57. package/dist/adapters/cli/oh-my-pi.js.map +1 -1
  58. package/dist/adapters/cli/shared-hints.d.ts +7 -4
  59. package/dist/adapters/cli/shared-hints.d.ts.map +1 -1
  60. package/dist/adapters/cli/shared-hints.js +52 -35
  61. package/dist/adapters/cli/shared-hints.js.map +1 -1
  62. package/dist/adapters/cli/strict-input-handle.d.ts +13 -0
  63. package/dist/adapters/cli/strict-input-handle.d.ts.map +1 -0
  64. package/dist/adapters/cli/strict-input-handle.js +39 -0
  65. package/dist/adapters/cli/strict-input-handle.js.map +1 -0
  66. package/dist/adapters/cli/traex.d.ts.map +1 -1
  67. package/dist/adapters/cli/traex.js +6 -4
  68. package/dist/adapters/cli/traex.js.map +1 -1
  69. package/dist/adapters/cli/types.d.ts +16 -0
  70. package/dist/adapters/cli/types.d.ts.map +1 -1
  71. package/dist/bot-registry.d.ts +137 -8
  72. package/dist/bot-registry.d.ts.map +1 -1
  73. package/dist/bot-registry.js +298 -12
  74. package/dist/bot-registry.js.map +1 -1
  75. package/dist/cli/session-list-liveness.d.ts +1 -1
  76. package/dist/cli/session-list-liveness.js +1 -1
  77. package/dist/cli/zmx-managed-attach.d.ts +41 -0
  78. package/dist/cli/zmx-managed-attach.d.ts.map +1 -0
  79. package/dist/cli/zmx-managed-attach.js +72 -0
  80. package/dist/cli/zmx-managed-attach.js.map +1 -0
  81. package/dist/cli.d.ts.map +1 -1
  82. package/dist/cli.js +780 -131
  83. package/dist/cli.js.map +1 -1
  84. package/dist/codex-app-runner.js +90 -2
  85. package/dist/codex-app-runner.js.map +1 -1
  86. package/dist/codex-rpc-engine.d.ts.map +1 -1
  87. package/dist/codex-rpc-engine.js +22 -6
  88. package/dist/codex-rpc-engine.js.map +1 -1
  89. package/dist/config.d.ts +5 -0
  90. package/dist/config.d.ts.map +1 -1
  91. package/dist/config.js +29 -1
  92. package/dist/config.js.map +1 -1
  93. package/dist/core/ask-broker.d.ts +22 -1
  94. package/dist/core/ask-broker.d.ts.map +1 -1
  95. package/dist/core/ask-broker.js +12 -5
  96. package/dist/core/ask-broker.js.map +1 -1
  97. package/dist/core/botmux-wrapper.d.ts +18 -0
  98. package/dist/core/botmux-wrapper.d.ts.map +1 -1
  99. package/dist/core/botmux-wrapper.js +25 -1
  100. package/dist/core/botmux-wrapper.js.map +1 -1
  101. package/dist/core/chat-rename.d.ts +27 -0
  102. package/dist/core/chat-rename.d.ts.map +1 -0
  103. package/dist/core/chat-rename.js +53 -0
  104. package/dist/core/chat-rename.js.map +1 -0
  105. package/dist/core/command-handler.d.ts.map +1 -1
  106. package/dist/core/command-handler.js +160 -12
  107. package/dist/core/command-handler.js.map +1 -1
  108. package/dist/core/cost-calculator.d.ts +25 -1
  109. package/dist/core/cost-calculator.d.ts.map +1 -1
  110. package/dist/core/cost-calculator.js +102 -4
  111. package/dist/core/cost-calculator.js.map +1 -1
  112. package/dist/core/dashboard-ipc-server.d.ts +16 -2
  113. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  114. package/dist/core/dashboard-ipc-server.js +611 -62
  115. package/dist/core/dashboard-ipc-server.js.map +1 -1
  116. package/dist/core/dashboard-rows.d.ts +22 -1
  117. package/dist/core/dashboard-rows.d.ts.map +1 -1
  118. package/dist/core/dashboard-rows.js +61 -0
  119. package/dist/core/dashboard-rows.js.map +1 -1
  120. package/dist/core/dashboard-url.d.ts +6 -0
  121. package/dist/core/dashboard-url.d.ts.map +1 -1
  122. package/dist/core/dashboard-url.js +11 -1
  123. package/dist/core/dashboard-url.js.map +1 -1
  124. package/dist/core/device-isolation-daemon.d.ts +28 -6
  125. package/dist/core/device-isolation-daemon.d.ts.map +1 -1
  126. package/dist/core/device-isolation-daemon.js +96 -13
  127. package/dist/core/device-isolation-daemon.js.map +1 -1
  128. package/dist/core/host-overload-alert.d.ts +75 -0
  129. package/dist/core/host-overload-alert.d.ts.map +1 -1
  130. package/dist/core/host-overload-alert.js +103 -0
  131. package/dist/core/host-overload-alert.js.map +1 -1
  132. package/dist/core/idle-worker-sweeper.js +1 -1
  133. package/dist/core/idle-worker-sweeper.js.map +1 -1
  134. package/dist/core/inflight-input-tracker.d.ts +3 -0
  135. package/dist/core/inflight-input-tracker.d.ts.map +1 -1
  136. package/dist/core/inflight-input-tracker.js +9 -0
  137. package/dist/core/inflight-input-tracker.js.map +1 -1
  138. package/dist/core/passthrough-commands.d.ts.map +1 -1
  139. package/dist/core/passthrough-commands.js +10 -0
  140. package/dist/core/passthrough-commands.js.map +1 -1
  141. package/dist/core/persistent-backend.d.ts +32 -4
  142. package/dist/core/persistent-backend.d.ts.map +1 -1
  143. package/dist/core/persistent-backend.js +75 -6
  144. package/dist/core/persistent-backend.js.map +1 -1
  145. package/dist/core/plugins/mcp/gateway.js +2 -2
  146. package/dist/core/plugins/mcp/gateway.js.map +1 -1
  147. package/dist/core/plugins/runtime.d.ts +3 -0
  148. package/dist/core/plugins/runtime.d.ts.map +1 -1
  149. package/dist/core/plugins/runtime.js.map +1 -1
  150. package/dist/core/plugins/service-manager.d.ts +4 -1
  151. package/dist/core/plugins/service-manager.d.ts.map +1 -1
  152. package/dist/core/plugins/service-manager.js +11 -6
  153. package/dist/core/plugins/service-manager.js.map +1 -1
  154. package/dist/core/role-library.d.ts +48 -1
  155. package/dist/core/role-library.d.ts.map +1 -1
  156. package/dist/core/role-library.js +111 -3
  157. package/dist/core/role-library.js.map +1 -1
  158. package/dist/core/role-resolver.d.ts +14 -2
  159. package/dist/core/role-resolver.d.ts.map +1 -1
  160. package/dist/core/role-resolver.js +47 -4
  161. package/dist/core/role-resolver.js.map +1 -1
  162. package/dist/core/session-activity.d.ts +14 -0
  163. package/dist/core/session-activity.d.ts.map +1 -1
  164. package/dist/core/session-activity.js +46 -1
  165. package/dist/core/session-activity.js.map +1 -1
  166. package/dist/core/session-board.d.ts +5 -2
  167. package/dist/core/session-board.d.ts.map +1 -1
  168. package/dist/core/session-board.js +5 -2
  169. package/dist/core/session-board.js.map +1 -1
  170. package/dist/core/session-liveness.d.ts +6 -5
  171. package/dist/core/session-liveness.d.ts.map +1 -1
  172. package/dist/core/session-liveness.js +34 -13
  173. package/dist/core/session-liveness.js.map +1 -1
  174. package/dist/core/session-manager.d.ts +3 -2
  175. package/dist/core/session-manager.d.ts.map +1 -1
  176. package/dist/core/session-manager.js +397 -103
  177. package/dist/core/session-manager.js.map +1 -1
  178. package/dist/core/session-marker.d.ts +11 -2
  179. package/dist/core/session-marker.d.ts.map +1 -1
  180. package/dist/core/session-marker.js +50 -11
  181. package/dist/core/session-marker.js.map +1 -1
  182. package/dist/core/session-message-preview.d.ts +20 -0
  183. package/dist/core/session-message-preview.d.ts.map +1 -0
  184. package/dist/core/session-message-preview.js +188 -0
  185. package/dist/core/session-message-preview.js.map +1 -0
  186. package/dist/core/session-title.d.ts +5 -1
  187. package/dist/core/session-title.d.ts.map +1 -1
  188. package/dist/core/session-title.js +20 -3
  189. package/dist/core/session-title.js.map +1 -1
  190. package/dist/core/startup-commands.d.ts +1 -1
  191. package/dist/core/startup-commands.js +1 -1
  192. package/dist/core/terminal-url.d.ts.map +1 -1
  193. package/dist/core/terminal-url.js +3 -2
  194. package/dist/core/terminal-url.js.map +1 -1
  195. package/dist/core/trigger-session.d.ts.map +1 -1
  196. package/dist/core/trigger-session.js +75 -12
  197. package/dist/core/trigger-session.js.map +1 -1
  198. package/dist/core/types.d.ts +35 -0
  199. package/dist/core/types.d.ts.map +1 -1
  200. package/dist/core/types.js +23 -0
  201. package/dist/core/types.js.map +1 -1
  202. package/dist/core/worker-ipc.d.ts +11 -0
  203. package/dist/core/worker-ipc.d.ts.map +1 -0
  204. package/dist/core/worker-ipc.js +26 -0
  205. package/dist/core/worker-ipc.js.map +1 -0
  206. package/dist/core/worker-pool.d.ts +179 -29
  207. package/dist/core/worker-pool.d.ts.map +1 -1
  208. package/dist/core/worker-pool.js +2118 -331
  209. package/dist/core/worker-pool.js.map +1 -1
  210. package/dist/daemon.d.ts +132 -3
  211. package/dist/daemon.d.ts.map +1 -1
  212. package/dist/daemon.js +1327 -449
  213. package/dist/daemon.js.map +1 -1
  214. package/dist/dashboard/auth.d.ts +2 -0
  215. package/dist/dashboard/auth.d.ts.map +1 -1
  216. package/dist/dashboard/auth.js +9 -0
  217. package/dist/dashboard/auth.js.map +1 -1
  218. package/dist/dashboard/bot-payload.d.ts +4 -0
  219. package/dist/dashboard/bot-payload.d.ts.map +1 -1
  220. package/dist/dashboard/bot-payload.js +5 -0
  221. package/dist/dashboard/bot-payload.js.map +1 -1
  222. package/dist/dashboard/federation-api.d.ts.map +1 -1
  223. package/dist/dashboard/federation-api.js +4 -0
  224. package/dist/dashboard/federation-api.js.map +1 -1
  225. package/dist/dashboard/federation-spoke-api.d.ts.map +1 -1
  226. package/dist/dashboard/federation-spoke-api.js +32 -5
  227. package/dist/dashboard/federation-spoke-api.js.map +1 -1
  228. package/dist/dashboard/public-redact.d.ts +8 -2
  229. package/dist/dashboard/public-redact.d.ts.map +1 -1
  230. package/dist/dashboard/public-redact.js +34 -7
  231. package/dist/dashboard/public-redact.js.map +1 -1
  232. package/dist/dashboard/session-card-model.d.ts.map +1 -1
  233. package/dist/dashboard/session-card-model.js +10 -2
  234. package/dist/dashboard/session-card-model.js.map +1 -1
  235. package/dist/dashboard/settings-write-applier.d.ts +35 -1
  236. package/dist/dashboard/settings-write-applier.d.ts.map +1 -1
  237. package/dist/dashboard/settings-write-applier.js +88 -1
  238. package/dist/dashboard/settings-write-applier.js.map +1 -1
  239. package/dist/dashboard/web/app.d.ts.map +1 -1
  240. package/dist/dashboard/web/app.js +0 -1
  241. package/dist/dashboard/web/app.js.map +1 -1
  242. package/dist/dashboard/web/bot-defaults-page.d.ts +15 -0
  243. package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -1
  244. package/dist/dashboard/web/bot-defaults-page.js +212 -58
  245. package/dist/dashboard/web/bot-defaults-page.js.map +1 -1
  246. package/dist/dashboard/web/bot-defaults.d.ts +3 -0
  247. package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
  248. package/dist/dashboard/web/bot-defaults.js.map +1 -1
  249. package/dist/dashboard/web/bot-onboarding.d.ts +2 -1
  250. package/dist/dashboard/web/bot-onboarding.d.ts.map +1 -1
  251. package/dist/dashboard/web/bot-onboarding.js.map +1 -1
  252. package/dist/dashboard/web/clipboard.d.ts +2 -0
  253. package/dist/dashboard/web/clipboard.d.ts.map +1 -0
  254. package/dist/dashboard/web/clipboard.js +42 -0
  255. package/dist/dashboard/web/clipboard.js.map +1 -0
  256. package/dist/dashboard/web/connectors-page.d.ts.map +1 -1
  257. package/dist/dashboard/web/connectors-page.js +12 -8
  258. package/dist/dashboard/web/connectors-page.js.map +1 -1
  259. package/dist/dashboard/web/dashboard-components.d.ts +17 -16
  260. package/dist/dashboard/web/dashboard-components.d.ts.map +1 -1
  261. package/dist/dashboard/web/dashboard-components.js.map +1 -1
  262. package/dist/dashboard/web/groups-page.d.ts.map +1 -1
  263. package/dist/dashboard/web/groups-page.js +7 -3
  264. package/dist/dashboard/web/groups-page.js.map +1 -1
  265. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  266. package/dist/dashboard/web/i18n.js +205 -12
  267. package/dist/dashboard/web/i18n.js.map +1 -1
  268. package/dist/dashboard/web/icon-tooltip.d.ts.map +1 -1
  269. package/dist/dashboard/web/icon-tooltip.js +16 -1
  270. package/dist/dashboard/web/icon-tooltip.js.map +1 -1
  271. package/dist/dashboard/web/listener-filters.d.ts +23 -0
  272. package/dist/dashboard/web/listener-filters.d.ts.map +1 -0
  273. package/dist/dashboard/web/listener-filters.js +36 -0
  274. package/dist/dashboard/web/listener-filters.js.map +1 -0
  275. package/dist/dashboard/web/overview-page.d.ts.map +1 -1
  276. package/dist/dashboard/web/overview-page.js.map +1 -1
  277. package/dist/dashboard/web/plugin-page.d.ts.map +1 -1
  278. package/dist/dashboard/web/plugin-page.js.map +1 -1
  279. package/dist/dashboard/web/preview-markdown.d.ts +5 -0
  280. package/dist/dashboard/web/preview-markdown.d.ts.map +1 -0
  281. package/dist/dashboard/web/preview-markdown.js +52 -0
  282. package/dist/dashboard/web/preview-markdown.js.map +1 -0
  283. package/dist/dashboard/web/roles-page.d.ts.map +1 -1
  284. package/dist/dashboard/web/roles-page.js +535 -10
  285. package/dist/dashboard/web/roles-page.js.map +1 -1
  286. package/dist/dashboard/web/roles.d.ts +77 -0
  287. package/dist/dashboard/web/roles.d.ts.map +1 -1
  288. package/dist/dashboard/web/roles.js +98 -0
  289. package/dist/dashboard/web/roles.js.map +1 -1
  290. package/dist/dashboard/web/session-terminal.d.ts.map +1 -1
  291. package/dist/dashboard/web/session-terminal.js +6 -4
  292. package/dist/dashboard/web/session-terminal.js.map +1 -1
  293. package/dist/dashboard/web/sessions-kanban.d.ts +4 -1
  294. package/dist/dashboard/web/sessions-kanban.d.ts.map +1 -1
  295. package/dist/dashboard/web/sessions-kanban.js +1 -1
  296. package/dist/dashboard/web/sessions-kanban.js.map +1 -1
  297. package/dist/dashboard/web/sessions-page.d.ts +3 -2
  298. package/dist/dashboard/web/sessions-page.d.ts.map +1 -1
  299. package/dist/dashboard/web/sessions-page.js +163 -28
  300. package/dist/dashboard/web/sessions-page.js.map +1 -1
  301. package/dist/dashboard/web/sessions.d.ts +25 -0
  302. package/dist/dashboard/web/sessions.d.ts.map +1 -1
  303. package/dist/dashboard/web/sessions.js +58 -7
  304. package/dist/dashboard/web/sessions.js.map +1 -1
  305. package/dist/dashboard/web/settings-page.d.ts +28 -3
  306. package/dist/dashboard/web/settings-page.d.ts.map +1 -1
  307. package/dist/dashboard/web/settings-page.js +61 -1
  308. package/dist/dashboard/web/settings-page.js.map +1 -1
  309. package/dist/dashboard/web/skills-page.d.ts +6 -5
  310. package/dist/dashboard/web/skills-page.d.ts.map +1 -1
  311. package/dist/dashboard/web/skills-page.js +2 -0
  312. package/dist/dashboard/web/skills-page.js.map +1 -1
  313. package/dist/dashboard/web/v3-components.d.ts +3 -2
  314. package/dist/dashboard/web/v3-components.d.ts.map +1 -1
  315. package/dist/dashboard/web/v3-components.js.map +1 -1
  316. package/dist/dashboard/web/vc-consumer-profiles-section.d.ts +12 -2
  317. package/dist/dashboard/web/vc-consumer-profiles-section.d.ts.map +1 -1
  318. package/dist/dashboard/web/vc-consumer-profiles-section.js +0 -9
  319. package/dist/dashboard/web/vc-consumer-profiles-section.js.map +1 -1
  320. package/dist/dashboard/web/webhook-logs-page.d.ts +2 -1
  321. package/dist/dashboard/web/webhook-logs-page.d.ts.map +1 -1
  322. package/dist/dashboard/web/webhook-logs-page.js +4 -1
  323. package/dist/dashboard/web/webhook-logs-page.js.map +1 -1
  324. package/dist/dashboard-web/app.js +1 -1
  325. package/dist/dashboard-web/chunks/bot-defaults-page-FAPIMQV3.js +10 -0
  326. package/dist/dashboard-web/chunks/{chunk-QFMH6ITB.js → chunk-5CJMHTT4.js} +1 -1
  327. package/dist/dashboard-web/chunks/{chunk-KQB42ML5.js → chunk-DLZK52JU.js} +14 -12
  328. package/dist/dashboard-web/chunks/chunk-FDZ7WOBW.js +16 -0
  329. package/dist/dashboard-web/chunks/{chunk-3QAUJXAS.js → chunk-GTAJV5YC.js} +1 -1
  330. package/dist/dashboard-web/chunks/{chunk-FNPGOZMX.js → chunk-IHNG6KE6.js} +1 -1
  331. package/dist/dashboard-web/chunks/{chunk-FLGXZJTD.js → chunk-VA6QWSGQ.js} +1 -1
  332. package/dist/dashboard-web/chunks/chunk-XJ7BGPJH.js +1 -0
  333. package/dist/dashboard-web/chunks/chunk-ZJVTGU5X.js +1 -0
  334. package/dist/dashboard-web/chunks/connectors-page-LKWRT3GS.js +1 -0
  335. package/dist/dashboard-web/chunks/groups-page-V6654ICQ.js +8 -0
  336. package/dist/dashboard-web/chunks/insights-page-237RMFQ5.js +7 -0
  337. package/dist/dashboard-web/chunks/{monitor-room-7IVIS75W.js → monitor-room-OO5Y4FRQ.js} +1 -1
  338. package/dist/dashboard-web/chunks/{monitoring-page-5SSJRQZF.js → monitoring-page-OUEITM5P.js} +1 -1
  339. package/dist/dashboard-web/chunks/{office-page-EOND3L5D.js → office-page-4KNAHQ4Q.js} +1 -1
  340. package/dist/dashboard-web/chunks/{overview-page-NISBVBBJ.js → overview-page-QDP4YK2S.js} +1 -1
  341. package/dist/dashboard-web/chunks/{plugin-page-JXC5F5YV.js → plugin-page-VEGTCEAS.js} +1 -1
  342. package/dist/dashboard-web/chunks/roles-page-PGXNZZNP.js +1 -0
  343. package/dist/dashboard-web/chunks/{schedules-page-AVOOKLUH.js → schedules-page-BJNC5TID.js} +1 -1
  344. package/dist/dashboard-web/chunks/sessions-page-M2ZYYCMO.js +6 -0
  345. package/dist/dashboard-web/chunks/settings-page-IJFZKUUW.js +2 -0
  346. package/dist/dashboard-web/chunks/{skills-page-5MW54PCC.js → skills-page-YRAIRZOQ.js} +3 -3
  347. package/dist/dashboard-web/chunks/{team-federation-page-2SNIMVXI.js → team-federation-page-DEU27GRU.js} +1 -1
  348. package/dist/dashboard-web/chunks/{v3-page-W6SBXXQC.js → v3-page-6KHEW2YL.js} +5 -5
  349. package/dist/dashboard-web/chunks/{whiteboards-page-STC54KTP.js → whiteboards-page-VDECAALH.js} +1 -1
  350. package/dist/dashboard-web/style.css +12545 -11432
  351. package/dist/dashboard.js +215 -36
  352. package/dist/dashboard.js.map +1 -1
  353. package/dist/global-config.d.ts +41 -2
  354. package/dist/global-config.d.ts.map +1 -1
  355. package/dist/global-config.js +47 -0
  356. package/dist/global-config.js.map +1 -1
  357. package/dist/i18n/en.d.ts.map +1 -1
  358. package/dist/i18n/en.js +71 -19
  359. package/dist/i18n/en.js.map +1 -1
  360. package/dist/i18n/zh.d.ts.map +1 -1
  361. package/dist/i18n/zh.js +71 -19
  362. package/dist/i18n/zh.js.map +1 -1
  363. package/dist/im/lark/callback-button-marker.d.ts +34 -0
  364. package/dist/im/lark/callback-button-marker.d.ts.map +1 -0
  365. package/dist/im/lark/callback-button-marker.js +99 -0
  366. package/dist/im/lark/callback-button-marker.js.map +1 -0
  367. package/dist/im/lark/card-builder.d.ts +46 -10
  368. package/dist/im/lark/card-builder.d.ts.map +1 -1
  369. package/dist/im/lark/card-builder.js +299 -78
  370. package/dist/im/lark/card-builder.js.map +1 -1
  371. package/dist/im/lark/card-handler.d.ts +3 -4
  372. package/dist/im/lark/card-handler.d.ts.map +1 -1
  373. package/dist/im/lark/card-handler.js +380 -148
  374. package/dist/im/lark/card-handler.js.map +1 -1
  375. package/dist/im/lark/client.d.ts +24 -1
  376. package/dist/im/lark/client.d.ts.map +1 -1
  377. package/dist/im/lark/client.js +101 -8
  378. package/dist/im/lark/client.js.map +1 -1
  379. package/dist/im/lark/doc-comment.d.ts.map +1 -1
  380. package/dist/im/lark/doc-comment.js +6 -1
  381. package/dist/im/lark/doc-comment.js.map +1 -1
  382. package/dist/im/lark/event-dispatcher.d.ts +115 -3
  383. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  384. package/dist/im/lark/event-dispatcher.js +534 -147
  385. package/dist/im/lark/event-dispatcher.js.map +1 -1
  386. package/dist/im/lark/grant-command.d.ts +16 -12
  387. package/dist/im/lark/grant-command.d.ts.map +1 -1
  388. package/dist/im/lark/grant-command.js +48 -151
  389. package/dist/im/lark/grant-command.js.map +1 -1
  390. package/dist/im/lark/grant-pending.d.ts +12 -4
  391. package/dist/im/lark/grant-pending.d.ts.map +1 -1
  392. package/dist/im/lark/grant-pending.js +31 -8
  393. package/dist/im/lark/grant-pending.js.map +1 -1
  394. package/dist/im/lark/invite-command.d.ts +20 -0
  395. package/dist/im/lark/invite-command.d.ts.map +1 -0
  396. package/dist/im/lark/invite-command.js +239 -0
  397. package/dist/im/lark/invite-command.js.map +1 -0
  398. package/dist/im/lark/md-card.d.ts +66 -4
  399. package/dist/im/lark/md-card.d.ts.map +1 -1
  400. package/dist/im/lark/md-card.js +201 -31
  401. package/dist/im/lark/md-card.js.map +1 -1
  402. package/dist/im/lark/mention-targets.d.ts +43 -0
  403. package/dist/im/lark/mention-targets.d.ts.map +1 -0
  404. package/dist/im/lark/mention-targets.js +190 -0
  405. package/dist/im/lark/mention-targets.js.map +1 -0
  406. package/dist/im/lark/message-parser.d.ts +31 -0
  407. package/dist/im/lark/message-parser.d.ts.map +1 -1
  408. package/dist/im/lark/message-parser.js +305 -32
  409. package/dist/im/lark/message-parser.js.map +1 -1
  410. package/dist/im/lark/reply-card-footer-signature.d.ts +7 -0
  411. package/dist/im/lark/reply-card-footer-signature.d.ts.map +1 -0
  412. package/dist/im/lark/reply-card-footer-signature.js +7 -0
  413. package/dist/im/lark/reply-card-footer-signature.js.map +1 -0
  414. package/dist/im/lark/sessions-card.d.ts.map +1 -1
  415. package/dist/im/lark/sessions-card.js +4 -1
  416. package/dist/im/lark/sessions-card.js.map +1 -1
  417. package/dist/im/lark/substitute-command.d.ts.map +1 -1
  418. package/dist/im/lark/substitute-command.js +8 -0
  419. package/dist/im/lark/substitute-command.js.map +1 -1
  420. package/dist/im/lark/topic-root-context.d.ts +35 -0
  421. package/dist/im/lark/topic-root-context.d.ts.map +1 -0
  422. package/dist/im/lark/topic-root-context.js +37 -0
  423. package/dist/im/lark/topic-root-context.js.map +1 -0
  424. package/dist/im/lark/v3-blocked-card.d.ts.map +1 -1
  425. package/dist/im/lark/v3-blocked-card.js +2 -1
  426. package/dist/im/lark/v3-blocked-card.js.map +1 -1
  427. package/dist/im/lark/v3-gate-card.d.ts.map +1 -1
  428. package/dist/im/lark/v3-gate-card.js +2 -1
  429. package/dist/im/lark/v3-gate-card.js.map +1 -1
  430. package/dist/im/lark/v3-loop-grant-card.d.ts.map +1 -1
  431. package/dist/im/lark/v3-loop-grant-card.js +2 -1
  432. package/dist/im/lark/v3-loop-grant-card.js.map +1 -1
  433. package/dist/im/lark/v3-progress-card.d.ts.map +1 -1
  434. package/dist/im/lark/v3-progress-card.js +2 -1
  435. package/dist/im/lark/v3-progress-card.js.map +1 -1
  436. package/dist/im/lark/v3-revisit-grant-card.d.ts.map +1 -1
  437. package/dist/im/lark/v3-revisit-grant-card.js +2 -1
  438. package/dist/im/lark/v3-revisit-grant-card.js.map +1 -1
  439. package/dist/index-core-only.d.ts +3 -0
  440. package/dist/index-core-only.d.ts.map +1 -0
  441. package/dist/index-core-only.js +107 -0
  442. package/dist/index-core-only.js.map +1 -0
  443. package/dist/services/async-trigger-state.d.ts +8 -0
  444. package/dist/services/async-trigger-state.d.ts.map +1 -1
  445. package/dist/services/async-trigger-state.js +3 -2
  446. package/dist/services/async-trigger-state.js.map +1 -1
  447. package/dist/services/async-trigger-store.d.ts +10 -2
  448. package/dist/services/async-trigger-store.d.ts.map +1 -1
  449. package/dist/services/async-trigger-store.js +3 -2
  450. package/dist/services/async-trigger-store.js.map +1 -1
  451. package/dist/services/backend-availability.d.ts +3 -1
  452. package/dist/services/backend-availability.d.ts.map +1 -1
  453. package/dist/services/backend-availability.js +14 -1
  454. package/dist/services/backend-availability.js.map +1 -1
  455. package/dist/services/backend-type-store.d.ts.map +1 -1
  456. package/dist/services/backend-type-store.js +1 -1
  457. package/dist/services/backend-type-store.js.map +1 -1
  458. package/dist/services/bot-config-store.d.ts.map +1 -1
  459. package/dist/services/bot-config-store.js +2 -1
  460. package/dist/services/bot-config-store.js.map +1 -1
  461. package/dist/services/bridge-fallback-gate.d.ts +14 -1
  462. package/dist/services/bridge-fallback-gate.d.ts.map +1 -1
  463. package/dist/services/bridge-fallback-gate.js +65 -1
  464. package/dist/services/bridge-fallback-gate.js.map +1 -1
  465. package/dist/services/card-prefs-store.d.ts +7 -2
  466. package/dist/services/card-prefs-store.d.ts.map +1 -1
  467. package/dist/services/card-prefs-store.js +30 -4
  468. package/dist/services/card-prefs-store.js.map +1 -1
  469. package/dist/services/codex-app-runner-protocol.d.ts +9 -0
  470. package/dist/services/codex-app-runner-protocol.d.ts.map +1 -1
  471. package/dist/services/codex-app-runner-protocol.js +17 -0
  472. package/dist/services/codex-app-runner-protocol.js.map +1 -1
  473. package/dist/services/codex-app-token-usage.d.ts +93 -0
  474. package/dist/services/codex-app-token-usage.d.ts.map +1 -0
  475. package/dist/services/codex-app-token-usage.js +190 -0
  476. package/dist/services/codex-app-token-usage.js.map +1 -0
  477. package/dist/services/codex-transcript.d.ts +6 -1
  478. package/dist/services/codex-transcript.d.ts.map +1 -1
  479. package/dist/services/codex-transcript.js +103 -32
  480. package/dist/services/codex-transcript.js.map +1 -1
  481. package/dist/services/federation-roster.d.ts +4 -0
  482. package/dist/services/federation-roster.d.ts.map +1 -1
  483. package/dist/services/federation-roster.js +3 -0
  484. package/dist/services/federation-roster.js.map +1 -1
  485. package/dist/services/federation-store.d.ts +6 -0
  486. package/dist/services/federation-store.d.ts.map +1 -1
  487. package/dist/services/federation-store.js.map +1 -1
  488. package/dist/services/grant-policy.d.ts +13 -0
  489. package/dist/services/grant-policy.d.ts.map +1 -0
  490. package/dist/services/grant-policy.js +40 -0
  491. package/dist/services/grant-policy.js.map +1 -0
  492. package/dist/services/grant-store.d.ts +24 -3
  493. package/dist/services/grant-store.d.ts.map +1 -1
  494. package/dist/services/grant-store.js +196 -12
  495. package/dist/services/grant-store.js.map +1 -1
  496. package/dist/services/groups-store.d.ts +36 -0
  497. package/dist/services/groups-store.d.ts.map +1 -1
  498. package/dist/services/groups-store.js +123 -1
  499. package/dist/services/groups-store.js.map +1 -1
  500. package/dist/services/jsonl-cursor.d.ts.map +1 -1
  501. package/dist/services/jsonl-cursor.js +8 -2
  502. package/dist/services/jsonl-cursor.js.map +1 -1
  503. package/dist/services/local-cli-opener.d.ts.map +1 -1
  504. package/dist/services/local-cli-opener.js +32 -0
  505. package/dist/services/local-cli-opener.js.map +1 -1
  506. package/dist/services/message-listener-run-preview-store.d.ts +43 -0
  507. package/dist/services/message-listener-run-preview-store.d.ts.map +1 -0
  508. package/dist/services/message-listener-run-preview-store.js +152 -0
  509. package/dist/services/message-listener-run-preview-store.js.map +1 -0
  510. package/dist/services/message-listener-store.d.ts +26 -0
  511. package/dist/services/message-listener-store.d.ts.map +1 -0
  512. package/dist/services/message-listener-store.js +138 -0
  513. package/dist/services/message-listener-store.js.map +1 -0
  514. package/dist/services/message-listener.d.ts +112 -0
  515. package/dist/services/message-listener.d.ts.map +1 -0
  516. package/dist/services/message-listener.js +391 -0
  517. package/dist/services/message-listener.js.map +1 -0
  518. package/dist/services/overload-alert-migration.d.ts +15 -0
  519. package/dist/services/overload-alert-migration.d.ts.map +1 -0
  520. package/dist/services/overload-alert-migration.js +115 -0
  521. package/dist/services/overload-alert-migration.js.map +1 -0
  522. package/dist/services/send-policy.d.ts +38 -0
  523. package/dist/services/send-policy.d.ts.map +1 -1
  524. package/dist/services/send-policy.js +34 -0
  525. package/dist/services/send-policy.js.map +1 -1
  526. package/dist/services/session-store.d.ts +12 -1
  527. package/dist/services/session-store.d.ts.map +1 -1
  528. package/dist/services/session-store.js +44 -2
  529. package/dist/services/session-store.js.map +1 -1
  530. package/dist/services/skill-registry-store.d.ts.map +1 -1
  531. package/dist/services/skill-registry-store.js +87 -12
  532. package/dist/services/skill-registry-store.js.map +1 -1
  533. package/dist/services/substitute-mode-normalize.d.ts +3 -0
  534. package/dist/services/substitute-mode-normalize.d.ts.map +1 -1
  535. package/dist/services/substitute-mode-normalize.js +8 -0
  536. package/dist/services/substitute-mode-normalize.js.map +1 -1
  537. package/dist/services/substitute-mode-store.d.ts.map +1 -1
  538. package/dist/services/substitute-mode-store.js +8 -1
  539. package/dist/services/substitute-mode-store.js.map +1 -1
  540. package/dist/services/transcript-resolver.d.ts +6 -1
  541. package/dist/services/transcript-resolver.d.ts.map +1 -1
  542. package/dist/services/transcript-resolver.js +145 -29
  543. package/dist/services/transcript-resolver.js.map +1 -1
  544. package/dist/services/trigger-types.d.ts +16 -0
  545. package/dist/services/trigger-types.d.ts.map +1 -1
  546. package/dist/services/trigger-types.js +6 -0
  547. package/dist/services/trigger-types.js.map +1 -1
  548. package/dist/setup/bot-config-editor.js +2 -2
  549. package/dist/setup/bot-config-editor.js.map +1 -1
  550. package/dist/setup/ensure-zmx.d.ts +31 -0
  551. package/dist/setup/ensure-zmx.d.ts.map +1 -0
  552. package/dist/setup/ensure-zmx.js +161 -0
  553. package/dist/setup/ensure-zmx.js.map +1 -0
  554. package/dist/setup/index.d.ts +1 -1
  555. package/dist/setup/index.js +1 -1
  556. package/dist/setup/open-platform-automation.d.ts +9 -0
  557. package/dist/setup/open-platform-automation.d.ts.map +1 -1
  558. package/dist/setup/open-platform-automation.js +15 -2
  559. package/dist/setup/open-platform-automation.js.map +1 -1
  560. package/dist/setup/setup-args.d.ts +1 -1
  561. package/dist/setup/setup-args.d.ts.map +1 -1
  562. package/dist/setup/setup-args.js +1 -1
  563. package/dist/skills/definitions.d.ts.map +1 -1
  564. package/dist/skills/definitions.js +32 -0
  565. package/dist/skills/definitions.js.map +1 -1
  566. package/dist/skills/injection-mode.d.ts +6 -2
  567. package/dist/skills/injection-mode.d.ts.map +1 -1
  568. package/dist/skills/injection-mode.js +10 -5
  569. package/dist/skills/injection-mode.js.map +1 -1
  570. package/dist/types.d.ts +79 -3
  571. package/dist/types.d.ts.map +1 -1
  572. package/dist/utils/child-env.d.ts +1 -1
  573. package/dist/utils/child-env.d.ts.map +1 -1
  574. package/dist/utils/child-env.js +4 -0
  575. package/dist/utils/child-env.js.map +1 -1
  576. package/dist/utils/effort-confirm-dialog.d.ts +40 -0
  577. package/dist/utils/effort-confirm-dialog.d.ts.map +1 -0
  578. package/dist/utils/effort-confirm-dialog.js +74 -0
  579. package/dist/utils/effort-confirm-dialog.js.map +1 -0
  580. package/dist/utils/pending-input-queue.d.ts +56 -0
  581. package/dist/utils/pending-input-queue.d.ts.map +1 -1
  582. package/dist/utils/pending-input-queue.js +80 -0
  583. package/dist/utils/pending-input-queue.js.map +1 -1
  584. package/dist/utils/render-dimensions.d.ts +8 -0
  585. package/dist/utils/render-dimensions.d.ts.map +1 -1
  586. package/dist/utils/render-dimensions.js +10 -0
  587. package/dist/utils/render-dimensions.js.map +1 -1
  588. package/dist/utils/stuck-key-guard.d.ts +3 -2
  589. package/dist/utils/stuck-key-guard.d.ts.map +1 -1
  590. package/dist/utils/stuck-key-guard.js +3 -3
  591. package/dist/utils/stuck-key-guard.js.map +1 -1
  592. package/dist/utils/terminal-renderer.d.ts +6 -0
  593. package/dist/utils/terminal-renderer.d.ts.map +1 -1
  594. package/dist/utils/terminal-renderer.js +15 -0
  595. package/dist/utils/terminal-renderer.js.map +1 -1
  596. package/dist/utils/tui-input-delivery.d.ts +32 -0
  597. package/dist/utils/tui-input-delivery.d.ts.map +1 -0
  598. package/dist/utils/tui-input-delivery.js +53 -0
  599. package/dist/utils/tui-input-delivery.js.map +1 -0
  600. package/dist/utils/xml.d.ts +19 -0
  601. package/dist/utils/xml.d.ts.map +1 -0
  602. package/dist/utils/xml.js +26 -0
  603. package/dist/utils/xml.js.map +1 -0
  604. package/dist/worker.js +1852 -374
  605. package/dist/worker.js.map +1 -1
  606. package/dist/workflows/definition.d.ts +8 -8
  607. package/dist/workflows/events/payloads.d.ts +8 -8
  608. package/dist/workflows/events/schema.d.ts +160 -160
  609. package/dist/workflows/v3/session-relay-client.js +1 -1
  610. package/dist/workflows/v3/session-relay-client.js.map +1 -1
  611. package/dist/workflows/v3/worker-fence.d.ts.map +1 -1
  612. package/dist/workflows/v3/worker-fence.js +15 -10
  613. package/dist/workflows/v3/worker-fence.js.map +1 -1
  614. package/package.json +1 -1
  615. package/dist/dashboard-web/chunks/bot-defaults-page-UKA7K4JV.js +0 -10
  616. package/dist/dashboard-web/chunks/chunk-DFLNXQNT.js +0 -1
  617. package/dist/dashboard-web/chunks/connectors-page-A77YAB5Y.js +0 -1
  618. package/dist/dashboard-web/chunks/groups-page-RUZNVXQB.js +0 -8
  619. package/dist/dashboard-web/chunks/insights-page-RF7U7RTA.js +0 -22
  620. package/dist/dashboard-web/chunks/roles-page-BUMKD7Y4.js +0 -1
  621. package/dist/dashboard-web/chunks/sessions-page-6HLSEAB7.js +0 -2
  622. package/dist/dashboard-web/chunks/settings-page-MJDZHQ4X.js +0 -2
@@ -1,11 +1,94 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
3
3
  import { DropdownMenu, Html, LoadingState, RefreshIconButton } from './dashboard-components.js';
4
+ import { applyListenerFilterState, filterListenerTargets, listenerTargetStateFor, } from './listener-filters.js';
4
5
  import { useT } from './react-hooks.js';
5
6
  import { mountReactPage } from './react-mount.js';
6
7
  import { hasExplicitChatRole, summarizeGroupProfileMatches, } from './role-profile-match.js';
7
- import { applyRoleProfile, botInChatCount, botRoleCount, byteLength, deleteProfileEntry, deleteRole, entryForBot, filterRoleGroups, filterRoleProfiles, hashChatId, isValidProfileId, loadGroups, loadProfileEntries, loadProfileEntry, loadProfiles, loadRole, loadRoleProfileContext, MAX_ROLE_BYTES, roleKey, ROLE_WARN_BYTES, saveInjectMode, saveProfileEntry, saveRole, } from './roles.js';
8
+ import { applyRoleProfile, botInChatCount, botRoleCount, byteLength, deleteProfileEntry, deleteMessageListener, deleteRole, entryForBot, filterRoleGroups, filterRoleProfiles, formatListenerPreviewTime, hashChatId, isValidProfileId, loadGroupMemberDisplays, loadGroups, loadMessageListenerRunPreviewStatus, loadMessageListener, loadProfileEntries, loadProfileEntry, loadProfiles, loadRole, loadRoleProfileContext, MAX_MESSAGE_LISTENER_PROMPT_BYTES, MAX_ROLE_BYTES, MESSAGE_LISTENER_WARN_BYTES, DEFAULT_MESSAGE_LISTENER_PREVIEW_LIMIT, MAX_MESSAGE_LISTENER_PREVIEW_LIMIT, roleKey, ROLE_WARN_BYTES, saveInjectMode, saveMessageListener, saveProfileEntry, saveRole, previewMessageListener, runMessageListenerPreview, } from './roles.js';
8
9
  import { botAvatarHtml, loadNameMaps } from './ui.js';
10
+ const LISTENER_MESSAGE_TYPES = ['text', 'post', 'image', 'interactive'];
11
+ const DEFAULT_LISTENER = {
12
+ enabled: false,
13
+ prompt: '',
14
+ senderPolicy: {
15
+ mode: 'include_only',
16
+ includeSenderTypes: ['user'],
17
+ excludeSelf: true,
18
+ },
19
+ messagePolicy: {
20
+ includeMsgTypes: [...LISTENER_MESSAGE_TYPES],
21
+ scope: 'top_level',
22
+ },
23
+ };
24
+ function cloneListener(listener) {
25
+ return {
26
+ enabled: listener?.enabled === true,
27
+ name: listener?.name ?? '',
28
+ replyCardTitle: listener?.replyCardTitle ?? '',
29
+ workingDir: listener?.workingDir ?? '',
30
+ prompt: listener?.prompt ?? '',
31
+ senderPolicy: {
32
+ mode: 'include_only',
33
+ includeSenderOpenIds: [...(listener?.senderPolicy?.includeSenderOpenIds ?? [])],
34
+ excludeSenderOpenIds: [],
35
+ includeSenderTypes: [...(listener?.senderPolicy?.includeSenderTypes ?? DEFAULT_LISTENER.senderPolicy?.includeSenderTypes ?? [])],
36
+ excludeSenderTypes: [...(listener?.senderPolicy?.excludeSenderTypes ?? [])],
37
+ excludeSelf: listener?.senderPolicy?.excludeSelf !== false,
38
+ },
39
+ messagePolicy: {
40
+ includeMsgTypes: [...(listener?.messagePolicy?.includeMsgTypes ?? DEFAULT_LISTENER.messagePolicy?.includeMsgTypes ?? [])],
41
+ scope: 'top_level',
42
+ },
43
+ };
44
+ }
45
+ function listenerHasConfig(listener) {
46
+ return listener?.enabled === true && listener.prompt.trim().length > 0;
47
+ }
48
+ function groupHasAnyRoleOrListener(group) {
49
+ return group.memberBots.some(bot => bot.inChat && (bot.hasRole || bot.hasMessageListener));
50
+ }
51
+ function memberDisplayName(member, openId) {
52
+ return member?.name || openId;
53
+ }
54
+ function listenerSenderTypeMatches(member, listener) {
55
+ const includeTypes = new Set(listener.senderPolicy?.includeSenderTypes ?? DEFAULT_LISTENER.senderPolicy?.includeSenderTypes ?? []);
56
+ if (includeTypes.size === 0)
57
+ return true;
58
+ return (member.memberType === 'user' || member.memberType === 'bot') && includeTypes.has(member.memberType);
59
+ }
60
+ function mergeListenerRunPreviewResults(current, next) {
61
+ const merged = new Map();
62
+ for (const result of current ?? [])
63
+ merged.set(result.messageId, result);
64
+ for (const result of next)
65
+ merged.set(result.messageId, { ...merged.get(result.messageId), ...result });
66
+ return [...merged.values()];
67
+ }
68
+ function listenerRunPreviewStateClass(state, ok) {
69
+ if (!ok || state === 'failed')
70
+ return 'error';
71
+ if (state === 'replied')
72
+ return 'ok';
73
+ if (state === 'running')
74
+ return 'running';
75
+ return 'triggered';
76
+ }
77
+ function listenerForEditor(listener, members = []) {
78
+ const next = cloneListener(listener ?? DEFAULT_LISTENER);
79
+ if (listener?.senderPolicy?.mode !== 'all_except_excluded')
80
+ return next;
81
+ const excluded = new Set(listener.senderPolicy.excludeSenderOpenIds ?? []);
82
+ next.senderPolicy = {
83
+ ...(next.senderPolicy ?? {}),
84
+ mode: 'include_only',
85
+ includeSenderOpenIds: members
86
+ .filter(member => listenerSenderTypeMatches(member, next) && !excluded.has(member.openId))
87
+ .map(member => member.openId),
88
+ excludeSenderOpenIds: [],
89
+ };
90
+ return next;
91
+ }
9
92
  function useAliveRef() {
10
93
  const alive = useRef(true);
11
94
  useEffect(() => {
@@ -65,6 +148,19 @@ function RolesPage(props) {
65
148
  const [injectSaving, setInjectSaving] = useState(false);
66
149
  const [roleFlash, setRoleFlash] = useState(null);
67
150
  const [injectFlash, setInjectFlash] = useState(null);
151
+ const [groupEditorSection, setGroupEditorSection] = useState('role');
152
+ const [selectedListener, setSelectedListener] = useState(null);
153
+ const [editingListener, setEditingListener] = useState(() => cloneListener(DEFAULT_LISTENER));
154
+ const [listenerMembers, setListenerMembers] = useState([]);
155
+ const [listenerLoading, setListenerLoading] = useState(false);
156
+ const [listenerMembersLoading, setListenerMembersLoading] = useState(false);
157
+ const [listenerSaving, setListenerSaving] = useState(false);
158
+ const [listenerDeleting, setListenerDeleting] = useState(false);
159
+ const [listenerFlash, setListenerFlash] = useState(null);
160
+ const [listenerPreviewLimit, setListenerPreviewLimit] = useState(DEFAULT_MESSAGE_LISTENER_PREVIEW_LIMIT);
161
+ const [listenerPreviewStatus, setListenerPreviewStatus] = useState({ kind: 'idle' });
162
+ const listenerRunPollRef = useRef(null);
163
+ const listenerRunPollToken = useRef(0);
68
164
  const [selectedProfileId, setSelectedProfileId] = useState(null);
69
165
  const [selectedProfileBotId, setSelectedProfileBotId] = useState(null);
70
166
  const [profileEntries, setProfileEntries] = useState([]);
@@ -97,6 +193,13 @@ function RolesPage(props) {
97
193
  const filteredProfiles = useMemo(() => filterRoleProfiles(profiles, profileFilter), [profiles, profileFilter]);
98
194
  const roleByteLen = byteLength(editingContent);
99
195
  const profileByteLen = byteLength(profileEditingContent);
196
+ const listenerPromptByteLen = byteLength(editingListener.prompt);
197
+ const listenerMemberById = useMemo(() => {
198
+ const map = new Map();
199
+ for (const member of listenerMembers)
200
+ map.set(member.openId, member);
201
+ return map;
202
+ }, [listenerMembers]);
100
203
  const flash = useCallback((setter, text, isError = false) => {
101
204
  const id = Date.now() + Math.random();
102
205
  setter({ text, isError, id });
@@ -134,6 +237,33 @@ function RolesPage(props) {
134
237
  setProfiles(nextProfiles);
135
238
  return nextProfiles;
136
239
  }, [alive]);
240
+ const applyLoadedListener = useCallback((listener, members = []) => {
241
+ const next = listenerForEditor(listener ?? DEFAULT_LISTENER, members);
242
+ setSelectedListener(listener);
243
+ setEditingListener(next);
244
+ }, []);
245
+ const loadListenerForSelection = useCallback(async (botId, groupId, serial) => {
246
+ setListenerLoading(true);
247
+ setListenerMembersLoading(true);
248
+ setListenerMembers([]);
249
+ try {
250
+ const [listener, members] = await Promise.all([
251
+ loadMessageListener(botId, groupId).catch(() => cloneListener(DEFAULT_LISTENER)),
252
+ loadGroupMemberDisplays(botId, groupId).catch(() => []),
253
+ ]);
254
+ if (!alive.current || serial !== selectSerial.current)
255
+ return;
256
+ applyLoadedListener(listenerHasConfig(listener) ? listener : null, members);
257
+ setListenerMembers(members);
258
+ setListenerFlash(null);
259
+ }
260
+ finally {
261
+ if (alive.current && serial === selectSerial.current) {
262
+ setListenerLoading(false);
263
+ setListenerMembersLoading(false);
264
+ }
265
+ }
266
+ }, [alive, applyLoadedListener]);
137
267
  const loadInitial = useCallback(async () => {
138
268
  setLoadingTree(true);
139
269
  setProfileListLoading(true);
@@ -148,7 +278,7 @@ function RolesPage(props) {
148
278
  await loadNameMaps();
149
279
  if (!alive.current)
150
280
  return;
151
- setExpandedGroups(new Set(snapshot.groups.filter(group => botRoleCount(group) > 0).map(group => group.chatId)));
281
+ setExpandedGroups(new Set(snapshot.groups.filter(groupHasAnyRoleOrListener).map(group => group.chatId)));
152
282
  if (props.tab === 'profiles') {
153
283
  const requestedChatId = hashChatId();
154
284
  setSelectedApplyGroupId(current => {
@@ -177,6 +307,10 @@ function RolesPage(props) {
177
307
  useEffect(() => {
178
308
  void loadInitial();
179
309
  }, [loadInitial]);
310
+ useEffect(() => () => {
311
+ listenerRunPollToken.current += 1;
312
+ listenerRunPollRef.current = null;
313
+ }, []);
180
314
  useEffect(() => {
181
315
  if (selectedApplyGroupId || groups.length === 0)
182
316
  return;
@@ -193,14 +327,17 @@ function RolesPage(props) {
193
327
  const serial = ++selectSerial.current;
194
328
  setSelectedGroupId(groupId);
195
329
  setSelectedBotId(botId);
330
+ setRoleFlash(null);
331
+ setInjectFlash(null);
332
+ setListenerFlash(null);
333
+ applyLoadedListener(null);
196
334
  const role = await loadRole(botId, groupId);
197
335
  if (!alive.current || serial !== selectSerial.current)
198
336
  return;
199
337
  setSelectedRole(role);
200
338
  setEditingContent(role.content ?? '');
201
339
  setEditingInjectMode(role.injectMode === 'once' ? 'once' : 'every');
202
- setRoleFlash(null);
203
- setInjectFlash(null);
340
+ await loadListenerForSelection(botId, groupId, serial);
204
341
  }
205
342
  async function handleGroupRefresh() {
206
343
  const snapshot = await refreshGroups();
@@ -208,12 +345,14 @@ function RolesPage(props) {
208
345
  return;
209
346
  void refreshRoleContext(snapshot.groups, profiles);
210
347
  if (selectedGroupId && selectedBotId) {
348
+ const serial = ++selectSerial.current;
211
349
  const role = await loadRole(selectedBotId, selectedGroupId);
212
- if (!alive.current)
350
+ if (!alive.current || serial !== selectSerial.current)
213
351
  return;
214
352
  setSelectedRole(role);
215
353
  setEditingContent(role.content ?? '');
216
354
  setEditingInjectMode(role.injectMode === 'once' ? 'once' : 'every');
355
+ await loadListenerForSelection(selectedBotId, selectedGroupId, serial);
217
356
  }
218
357
  }
219
358
  async function handleSaveRole() {
@@ -287,6 +426,273 @@ function RolesPage(props) {
287
426
  setInjectSaving(false);
288
427
  }
289
428
  }
429
+ function updateEditingListener(patch) {
430
+ setEditingListener(prev => ({ ...prev, ...patch }));
431
+ }
432
+ function updateListenerSenderPolicy(patch) {
433
+ setEditingListener(prev => ({
434
+ ...prev,
435
+ senderPolicy: {
436
+ ...(prev.senderPolicy ?? {}),
437
+ ...patch,
438
+ },
439
+ }));
440
+ }
441
+ function updateListenerMessagePolicy(patch) {
442
+ setEditingListener(prev => ({
443
+ ...prev,
444
+ messagePolicy: {
445
+ ...(prev.messagePolicy ?? { scope: 'top_level' }),
446
+ ...patch,
447
+ scope: 'top_level',
448
+ },
449
+ }));
450
+ }
451
+ function toggleListenerSenderType(type, checked) {
452
+ setEditingListener(prev => {
453
+ const current = new Set(prev.senderPolicy?.includeSenderTypes ?? []);
454
+ if (checked)
455
+ current.add(type);
456
+ else {
457
+ if (current.size <= 1 && current.has(type))
458
+ return prev;
459
+ current.delete(type);
460
+ }
461
+ return {
462
+ ...prev,
463
+ senderPolicy: {
464
+ ...(prev.senderPolicy ?? {}),
465
+ includeSenderTypes: [...current],
466
+ },
467
+ };
468
+ });
469
+ }
470
+ function toggleListenerMsgType(msgType, checked) {
471
+ setEditingListener(prev => {
472
+ const current = new Set(prev.messagePolicy?.includeMsgTypes ?? []);
473
+ if (checked)
474
+ current.add(msgType);
475
+ else {
476
+ if (current.size <= 1 && current.has(msgType))
477
+ return prev;
478
+ current.delete(msgType);
479
+ }
480
+ return {
481
+ ...prev,
482
+ messagePolicy: {
483
+ ...(prev.messagePolicy ?? { scope: 'top_level' }),
484
+ includeMsgTypes: [...current],
485
+ scope: 'top_level',
486
+ },
487
+ };
488
+ });
489
+ }
490
+ function setListenerTargetPolicy(openId, listening) {
491
+ setEditingListener(prev => {
492
+ const next = applyListenerFilterState({
493
+ include: prev.senderPolicy?.includeSenderOpenIds ?? [],
494
+ exclude: prev.senderPolicy?.excludeSenderOpenIds ?? [],
495
+ targetIds: [openId],
496
+ listening,
497
+ });
498
+ return {
499
+ ...prev,
500
+ senderPolicy: {
501
+ ...(prev.senderPolicy ?? {}),
502
+ mode: 'include_only',
503
+ includeSenderOpenIds: next.include,
504
+ excludeSenderOpenIds: next.exclude,
505
+ },
506
+ };
507
+ });
508
+ }
509
+ function setListenerTargetsPolicy(openIds, listening) {
510
+ setEditingListener(prev => {
511
+ const current = applyListenerFilterState({
512
+ include: prev.senderPolicy?.includeSenderOpenIds ?? [],
513
+ exclude: prev.senderPolicy?.excludeSenderOpenIds ?? [],
514
+ targetIds: openIds,
515
+ listening,
516
+ });
517
+ return {
518
+ ...prev,
519
+ senderPolicy: {
520
+ ...(prev.senderPolicy ?? {}),
521
+ mode: 'include_only',
522
+ includeSenderOpenIds: current.include,
523
+ excludeSenderOpenIds: current.exclude,
524
+ },
525
+ };
526
+ });
527
+ }
528
+ function listenerSavePayload() {
529
+ const senderPolicy = editingListener.senderPolicy ?? {};
530
+ const messagePolicy = editingListener.messagePolicy ?? {};
531
+ const includeSenderOpenIds = [...new Set(senderPolicy.includeSenderOpenIds ?? [])].filter(Boolean);
532
+ const includeSenderTypes = [...new Set(senderPolicy.includeSenderTypes ?? [])].filter((type) => type === 'user' || type === 'bot');
533
+ const includeMsgTypes = [...new Set(messagePolicy.includeMsgTypes ?? [])].filter(Boolean);
534
+ return {
535
+ enabled: editingListener.enabled,
536
+ ...(editingListener.name?.trim() ? { name: editingListener.name.trim() } : {}),
537
+ ...(editingListener.replyCardTitle?.trim() ? { replyCardTitle: editingListener.replyCardTitle.trim() } : {}),
538
+ ...(editingListener.workingDir?.trim() ? { workingDir: editingListener.workingDir.trim() } : {}),
539
+ prompt: editingListener.prompt.trim(),
540
+ senderPolicy: {
541
+ ...(includeSenderOpenIds.length > 0 ? { includeSenderOpenIds } : {}),
542
+ mode: 'include_only',
543
+ ...(includeSenderTypes.length > 0 ? { includeSenderTypes } : {}),
544
+ excludeSelf: senderPolicy.excludeSelf !== false,
545
+ },
546
+ messagePolicy: {
547
+ ...(includeMsgTypes.length > 0 ? { includeMsgTypes } : {}),
548
+ scope: 'top_level',
549
+ },
550
+ };
551
+ }
552
+ function validateListenerForPreview() {
553
+ if (!editingListener.prompt.trim()) {
554
+ flash(setListenerFlash, tr('roles.listenerPromptRequired'), true);
555
+ return null;
556
+ }
557
+ if ((editingListener.senderPolicy?.includeSenderOpenIds?.length ?? 0) === 0) {
558
+ flash(setListenerFlash, tr('roles.listenerSenderRequired'), true);
559
+ return null;
560
+ }
561
+ return {
562
+ ...listenerSavePayload(),
563
+ enabled: true,
564
+ };
565
+ }
566
+ async function handleListenerPreview(run) {
567
+ if (!selectedGroupId || !selectedBotId)
568
+ return;
569
+ const payload = validateListenerForPreview();
570
+ if (!payload)
571
+ return;
572
+ const mode = run ? 'run' : 'preview';
573
+ setListenerPreviewStatus({ kind: 'loading', mode });
574
+ try {
575
+ const response = run
576
+ ? await runMessageListenerPreview(selectedBotId, selectedGroupId, payload, listenerPreviewLimit)
577
+ : await previewMessageListener(selectedBotId, selectedGroupId, payload, listenerPreviewLimit);
578
+ if (!alive.current)
579
+ return;
580
+ setListenerPreviewStatus(response.ok
581
+ ? { kind: 'result', response, mode }
582
+ : { kind: 'error', text: response.error || tr('roles.listenerPreviewFailed') });
583
+ if (response.ok && run && response.runId) {
584
+ startListenerRunPreviewPolling(response.runId);
585
+ }
586
+ }
587
+ catch (err) {
588
+ if (!alive.current)
589
+ return;
590
+ setListenerPreviewStatus({ kind: 'error', text: err instanceof Error ? err.message : tr('roles.listenerPreviewFailed') });
591
+ }
592
+ }
593
+ function startListenerRunPreviewPolling(runId) {
594
+ if (!selectedGroupId || !selectedBotId)
595
+ return;
596
+ const token = ++listenerRunPollToken.current;
597
+ listenerRunPollRef.current = { runId, token };
598
+ const poll = async () => {
599
+ if (!alive.current)
600
+ return;
601
+ const current = listenerRunPollRef.current;
602
+ if (!current || current.runId !== runId || current.token !== token || !selectedGroupId || !selectedBotId)
603
+ return;
604
+ try {
605
+ const status = await loadMessageListenerRunPreviewStatus(selectedBotId, selectedGroupId, runId);
606
+ if (!alive.current)
607
+ return;
608
+ if (status.ok && status.results) {
609
+ const nextResults = status.results;
610
+ setListenerPreviewStatus(previous => {
611
+ if (previous.kind !== 'result' || previous.mode !== 'run' || previous.response.runId !== runId)
612
+ return previous;
613
+ return {
614
+ ...previous,
615
+ response: {
616
+ ...previous.response,
617
+ results: mergeListenerRunPreviewResults(previous.response.results, nextResults),
618
+ },
619
+ };
620
+ });
621
+ if (nextResults.some(result => result.state === 'triggered' || result.state === 'running')) {
622
+ scheduleTimer(poll, 1500);
623
+ }
624
+ else if (listenerRunPollRef.current?.runId === runId) {
625
+ listenerRunPollRef.current = null;
626
+ }
627
+ }
628
+ }
629
+ catch {
630
+ scheduleTimer(poll, 2500);
631
+ }
632
+ };
633
+ scheduleTimer(poll, 1500);
634
+ }
635
+ async function handleSaveListener() {
636
+ if (!selectedGroupId || !selectedBotId)
637
+ return;
638
+ if (!editingListener.enabled) {
639
+ await handleDeleteListener(false);
640
+ return;
641
+ }
642
+ if (!editingListener.prompt.trim()) {
643
+ flash(setListenerFlash, tr('roles.listenerPromptRequired'), true);
644
+ return;
645
+ }
646
+ if ((editingListener.senderPolicy?.includeSenderOpenIds?.length ?? 0) === 0) {
647
+ flash(setListenerFlash, tr('roles.listenerSenderRequired'), true);
648
+ return;
649
+ }
650
+ setListenerSaving(true);
651
+ try {
652
+ const ok = await saveMessageListener(selectedBotId, selectedGroupId, listenerSavePayload());
653
+ if (!alive.current)
654
+ return;
655
+ if (ok) {
656
+ const snapshot = await refreshGroups();
657
+ if (!alive.current)
658
+ return;
659
+ const listener = await loadMessageListener(selectedBotId, selectedGroupId);
660
+ if (!alive.current)
661
+ return;
662
+ applyLoadedListener(listenerHasConfig(listener) ? listener : null);
663
+ void refreshRoleContext(snapshot.groups, profiles);
664
+ }
665
+ flash(setListenerFlash, ok ? tr('roles.saved') : tr('roles.saveFailed'), !ok);
666
+ }
667
+ finally {
668
+ if (alive.current)
669
+ setListenerSaving(false);
670
+ }
671
+ }
672
+ async function handleDeleteListener(confirmFirst = true) {
673
+ if (!selectedGroupId || !selectedBotId)
674
+ return;
675
+ if (confirmFirst && !confirm(tr('roles.listenerConfirmDelete')))
676
+ return;
677
+ setListenerDeleting(true);
678
+ try {
679
+ const ok = await deleteMessageListener(selectedBotId, selectedGroupId);
680
+ if (!alive.current)
681
+ return;
682
+ if (ok) {
683
+ const snapshot = await refreshGroups();
684
+ if (!alive.current)
685
+ return;
686
+ applyLoadedListener(null);
687
+ void refreshRoleContext(snapshot.groups, profiles);
688
+ }
689
+ flash(setListenerFlash, ok ? tr('roles.saved') : tr('roles.saveFailed'), !ok);
690
+ }
691
+ finally {
692
+ if (alive.current)
693
+ setListenerDeleting(false);
694
+ }
695
+ }
290
696
  async function handleSelectProfile(profileId) {
291
697
  const clean = profileId.trim();
292
698
  if (!isValidProfileId(clean))
@@ -439,13 +845,17 @@ function RolesPage(props) {
439
845
  }
440
846
  }
441
847
  const roleSaveDisabled = roleSaving || roleByteLen > MAX_ROLE_BYTES || editingContent.trim().length === 0;
848
+ const listenerSaveDisabled = listenerSaving
849
+ || listenerDeleting
850
+ || listenerPromptByteLen > MAX_MESSAGE_LISTENER_PROMPT_BYTES
851
+ || (editingListener.enabled && editingListener.prompt.trim().length === 0);
442
852
  const profileSaveDisabled = profileSaving || profileByteLen > MAX_ROLE_BYTES || profileEditingContent.trim().length === 0;
443
853
  const isProfiles = props.tab === 'profiles';
444
854
  const tabs = (_jsxs("nav", { className: "roles-subnav insight-tabs", role: "tablist", "aria-label": tr('roles.title'), children: [_jsx("a", { href: "#/roles", className: `itab${isProfiles ? '' : ' on'}`, role: "tab", "aria-selected": !isProfiles, children: tr('roles.tabGroups') }), _jsx("a", { href: "#/roles/profile", className: `itab${isProfiles ? ' on' : ''}`, role: "tab", "aria-selected": isProfiles, children: tr('roles.tabProfiles') })] }));
445
- return (_jsxs("section", { className: "page roles-page", children: [_jsxs("div", { className: "page-heading roles-heading", children: [_jsxs("div", { children: [_jsx("p", { className: "eyebrow", children: tr('nav.roles') }), _jsx("h1", { children: tr('roles.title') })] }), _jsx("div", { className: "page-heading-actions", children: tabs })] }), _jsxs("div", { id: "roles-by-group-view", className: "roles-layout", hidden: isProfiles, children: [_jsxs("div", { className: "roles-tree-panel", children: [_jsxs("div", { className: "roles-tree-header dashboard-toolbar", children: [_jsx("input", { type: "search", id: "roles-search", placeholder: tr('roles.search'), value: groupsFilter, onChange: ev => setGroupsFilter(ev.target.value) }), _jsx(RefreshIconButton, { id: "roles-refresh", label: tr('roles.refresh'), onClick: () => void handleGroupRefresh() })] }), _jsx("div", { id: "roles-tree", className: "roles-tree", children: loadingTree ? _jsx(LoadingState, { label: tr('common.loading') }) : (_jsx(GroupsTree, { groups: filteredGroups, profiles: profiles, context: roleContext, contextLoaded: roleContextLoaded, expandedGroups: expandedGroups, selectedGroupId: selectedGroupId, selectedBotId: selectedBotId, tr: tr, onToggleGroup: groupId => setExpandedGroups(prev => toggleSet(prev, groupId)), onSelectBot: (groupId, botId) => void handleSelectBot(groupId, botId) })) })] }), _jsx("div", { className: "roles-editor-panel", children: !selectedGroupId || !selectedBotId ? (_jsx("div", { id: "roles-editor-empty", className: "roles-editor-empty", children: tr('roles.selectHint') })) : (_jsxs("div", { id: "roles-editor-form", className: "roles-editor-form", children: [_jsxs("div", { className: "roles-editor-head", children: [_jsxs("div", { children: [_jsxs("div", { className: "roles-editor-breadcrumb", children: [_jsx("span", { id: "roles-editor-group-name", children: selectedGroup?.name ?? selectedGroupId }), _jsx("span", { className: "roles-breadcrumb-sep", children: "\u203A" }), _jsx("span", { id: "roles-editor-bot-name", children: selectedBot?.botName ?? selectedBotId })] }), _jsx("div", { className: "roles-editor-meta", children: _jsxs("span", { id: "roles-editor-chat-id", className: "roles-editor-meta-line", children: [selectedGroupId, " \u00B7 ", selectedBotId] }) })] }), _jsxs("div", { className: "roles-editor-actions roles-editor-head-actions", children: [_jsx("button", { type: "button", id: "roles-delete", className: "danger", style: { display: selectedRole?.hasRole ? '' : 'none' }, disabled: roleDeleting, onClick: () => void handleDeleteRole(), children: roleDeleting ? '...' : tr('roles.delete') }), _jsx("button", { type: "button", id: "roles-save", className: "primary", disabled: roleSaveDisabled, onClick: () => void handleSaveRole(), children: roleSaving ? '...' : tr('roles.save') })] })] }), _jsxs("div", { className: "roles-editor-inject", children: [_jsx("span", { className: "roles-field-label", children: tr('roles.injectModeLabel') }), _jsx(DropdownMenu, { id: "roles-editor-inject-mode", className: "roles-inline-menu", ariaLabel: tr('roles.injectModeLabel'), disabled: injectSaving, label: editingInjectMode === 'once' ? tr('roles.injectModeOnce') : tr('roles.injectModeEvery'), value: editingInjectMode, options: [
446
- { value: 'every', label: tr('roles.injectModeEvery') },
447
- { value: 'once', label: tr('roles.injectModeOnce') },
448
- ], onChange: mode => void handleInjectModeChange(mode === 'once' ? 'once' : 'every') }), _jsx("span", { className: "roles-editor-inject-hint", children: tr('roles.injectModeHint') }), _jsx(Flash, { flash: injectFlash })] }), _jsx("textarea", { id: "roles-editor-textarea", placeholder: tr('roles.editorPlaceholder'), rows: 14, value: editingContent, onChange: ev => setEditingContent(ev.target.value) }), _jsxs("div", { className: "roles-editor-footer", children: [_jsxs("span", { id: "roles-editor-bytecount", className: byteCountClass(roleByteLen), children: [roleByteLen, " / ", MAX_ROLE_BYTES, " bytes"] }), _jsx(Flash, { flash: roleFlash })] }), _jsx(RolePreview, { content: editingContent, tr: tr, id: "roles-preview" })] })) })] }), _jsxs("div", { id: "roles-profiles-view", className: "roles-layout roles-profiles-layout", hidden: !isProfiles, children: [_jsxs("div", { className: "roles-tree-panel", children: [_jsxs("div", { className: "roles-tree-header roles-profile-create dashboard-toolbar", children: [_jsx("input", { type: "text", id: "roles-profile-id", placeholder: tr('roles.profileIdPlaceholder'), maxLength: 64, ref: profileIdInputRef, onChange: ev => ev.currentTarget.setCustomValidity('') }), _jsx("button", { type: "button", id: "roles-profile-select", onClick: () => void handleOpenProfile(), children: tr('roles.openProfile') })] }), _jsxs("div", { className: "roles-tree-header dashboard-toolbar", children: [_jsx("input", { type: "search", id: "roles-profile-search", placeholder: tr('roles.profileSearch'), value: profileFilter, onChange: ev => setProfileFilter(ev.target.value) }), _jsx(RefreshIconButton, { id: "roles-profile-refresh", label: tr('roles.refresh'), onClick: () => void handleProfileRefresh() })] }), _jsx("div", { id: "roles-profile-list", className: "roles-tree", children: profileListLoading ? _jsx(LoadingState, { label: tr('common.loading') }) : (_jsx(ProfileList, { profiles: filteredProfiles, selectedProfileId: selectedProfileId, tr: tr, onSelect: profileId => void handleSelectProfile(profileId) })) })] }), _jsx("div", { className: "roles-editor-panel", children: !selectedProfileId ? (_jsx("div", { id: "roles-profile-empty", className: "roles-editor-empty", children: tr('roles.profileSelectHint') })) : (_jsxs("div", { id: "roles-profile-detail", className: "roles-editor-form roles-profile-detail", children: [_jsx("div", { className: "roles-profile-title", children: _jsxs("div", { children: [_jsxs("div", { className: "roles-editor-breadcrumb", children: [_jsx("span", { children: selectedProfileId }), selectedProfileBot ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "roles-breadcrumb-sep", children: "\u203A" }), _jsx("span", { children: selectedProfileBot.botName ?? selectedProfileBot.larkAppId })] })) : null] }), _jsx("div", { className: "roles-editor-meta-line", children: tr('roles.profileRuntimeHint') })] }) }), _jsxs("div", { className: "roles-profile-grid", children: [_jsxs("div", { className: "roles-profile-bots", children: [_jsx("div", { className: "roles-profile-section-title", children: tr('roles.profileBots') }), _jsx("div", { className: "roles-profile-bot-list", children: allBots.map(bot => {
855
+ return (_jsxs("section", { className: "page roles-page", children: [_jsxs("div", { className: "page-heading roles-heading", children: [_jsxs("div", { children: [_jsx("p", { className: "eyebrow", children: tr('nav.roles') }), _jsx("h1", { children: tr('roles.title') })] }), _jsx("div", { className: "page-heading-actions", children: tabs })] }), _jsxs("div", { id: "roles-by-group-view", className: "roles-layout", hidden: isProfiles, children: [_jsxs("div", { className: "roles-tree-panel", children: [_jsxs("div", { className: "roles-tree-header dashboard-toolbar", children: [_jsx("input", { type: "search", id: "roles-search", placeholder: tr('roles.search'), value: groupsFilter, onChange: ev => setGroupsFilter(ev.target.value) }), _jsx(RefreshIconButton, { id: "roles-refresh", label: tr('roles.refresh'), onClick: () => void handleGroupRefresh() })] }), _jsx("div", { id: "roles-tree", className: "roles-tree", children: loadingTree ? _jsx(LoadingState, { label: tr('common.loading') }) : (_jsx(GroupsTree, { groups: filteredGroups, profiles: profiles, context: roleContext, contextLoaded: roleContextLoaded, expandedGroups: expandedGroups, selectedGroupId: selectedGroupId, selectedBotId: selectedBotId, tr: tr, onToggleGroup: groupId => setExpandedGroups(prev => toggleSet(prev, groupId)), onSelectBot: (groupId, botId) => void handleSelectBot(groupId, botId) })) })] }), _jsx("div", { className: "roles-editor-panel", children: !selectedGroupId || !selectedBotId ? (_jsx("div", { id: "roles-editor-empty", className: "roles-editor-empty", children: tr('roles.selectHint') })) : (_jsxs("div", { id: "roles-editor-form", className: "roles-editor-form", children: [_jsxs("div", { className: "roles-editor-head", children: [_jsxs("div", { children: [_jsxs("div", { className: "roles-editor-breadcrumb", children: [_jsx("span", { id: "roles-editor-group-name", children: selectedGroup?.name ?? selectedGroupId }), _jsx("span", { className: "roles-breadcrumb-sep", children: "\u203A" }), _jsx("span", { id: "roles-editor-bot-name", children: selectedBot?.botName ?? selectedBotId })] }), _jsx("div", { className: "roles-editor-meta", children: _jsxs("span", { id: "roles-editor-chat-id", className: "roles-editor-meta-line", children: [selectedGroupId, " \u00B7 ", selectedBotId] }) })] }), _jsx("div", { className: "roles-editor-actions roles-editor-head-actions", children: groupEditorSection === 'role' ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", id: "roles-delete", className: "danger", style: { display: selectedRole?.hasRole ? '' : 'none' }, disabled: roleDeleting, onClick: () => void handleDeleteRole(), children: roleDeleting ? '...' : tr('roles.delete') }), _jsx("button", { type: "button", id: "roles-save", className: "primary", disabled: roleSaveDisabled, onClick: () => void handleSaveRole(), children: roleSaving ? '...' : tr('roles.save') })] })) : (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", id: "roles-listener-delete", className: "danger", style: { display: selectedListener?.enabled ? '' : 'none' }, disabled: listenerDeleting, onClick: () => void handleDeleteListener(), children: listenerDeleting ? '...' : tr('roles.delete') }), _jsx("button", { type: "button", id: "roles-listener-save", className: "primary", disabled: listenerSaveDisabled, onClick: () => void handleSaveListener(), children: listenerSaving ? '...' : tr('roles.save') })] })) })] }), _jsxs("div", { className: "roles-editor-switch segmented", role: "tablist", "aria-label": tr('roles.editorTabs'), children: [_jsx("button", { type: "button", className: groupEditorSection === 'role' ? 'active' : '', "aria-pressed": groupEditorSection === 'role', onClick: () => setGroupEditorSection('role'), children: tr('roles.roleTab') }), _jsx("button", { type: "button", className: groupEditorSection === 'listener' ? 'active' : '', "aria-pressed": groupEditorSection === 'listener', onClick: () => setGroupEditorSection('listener'), children: tr('roles.listenerTab') })] }), groupEditorSection === 'role' ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "roles-editor-inject", children: [_jsx("span", { className: "roles-field-label", children: tr('roles.injectModeLabel') }), _jsx(DropdownMenu, { id: "roles-editor-inject-mode", className: "roles-inline-menu", ariaLabel: tr('roles.injectModeLabel'), disabled: injectSaving, label: editingInjectMode === 'once' ? tr('roles.injectModeOnce') : tr('roles.injectModeEvery'), value: editingInjectMode, options: [
856
+ { value: 'every', label: tr('roles.injectModeEvery') },
857
+ { value: 'once', label: tr('roles.injectModeOnce') },
858
+ ], onChange: mode => void handleInjectModeChange(mode === 'once' ? 'once' : 'every') }), _jsx("span", { className: "roles-editor-inject-hint", children: tr('roles.injectModeHint') }), _jsx(Flash, { flash: injectFlash })] }), _jsx("textarea", { id: "roles-editor-textarea", placeholder: tr('roles.editorPlaceholder'), rows: 14, value: editingContent, onChange: ev => setEditingContent(ev.target.value) }), _jsxs("div", { className: "roles-editor-footer", children: [_jsxs("span", { id: "roles-editor-bytecount", className: byteCountClass(roleByteLen), children: [roleByteLen, " / ", MAX_ROLE_BYTES, " bytes"] }), _jsx(Flash, { flash: roleFlash })] }), _jsx(RolePreview, { content: editingContent, tr: tr, id: "roles-preview" })] })) : (_jsx(MessageListenerEditor, { listener: editingListener, members: listenerMembers, memberById: listenerMemberById, promptByteLen: listenerPromptByteLen, loading: listenerLoading, membersLoading: listenerMembersLoading, flash: listenerFlash, tr: tr, onPatch: updateEditingListener, onSenderPolicyPatch: updateListenerSenderPolicy, onMessagePolicyPatch: updateListenerMessagePolicy, onToggleSenderType: toggleListenerSenderType, onToggleMsgType: toggleListenerMsgType, onSetTargetPolicy: setListenerTargetPolicy, onSetTargetsPolicy: setListenerTargetsPolicy, previewLimit: listenerPreviewLimit, previewStatus: listenerPreviewStatus, onPreviewLimitChange: setListenerPreviewLimit, onPreview: () => void handleListenerPreview(false), onRunPreview: () => void handleListenerPreview(true) }))] })) })] }), _jsxs("div", { id: "roles-profiles-view", className: "roles-layout roles-profiles-layout", hidden: !isProfiles, children: [_jsxs("div", { className: "roles-tree-panel", children: [_jsxs("div", { className: "roles-tree-header roles-profile-create dashboard-toolbar", children: [_jsx("input", { type: "text", id: "roles-profile-id", placeholder: tr('roles.profileIdPlaceholder'), maxLength: 64, ref: profileIdInputRef, onChange: ev => ev.currentTarget.setCustomValidity('') }), _jsx("button", { type: "button", id: "roles-profile-select", onClick: () => void handleOpenProfile(), children: tr('roles.openProfile') })] }), _jsxs("div", { className: "roles-tree-header dashboard-toolbar", children: [_jsx("input", { type: "search", id: "roles-profile-search", placeholder: tr('roles.profileSearch'), value: profileFilter, onChange: ev => setProfileFilter(ev.target.value) }), _jsx(RefreshIconButton, { id: "roles-profile-refresh", label: tr('roles.refresh'), onClick: () => void handleProfileRefresh() })] }), _jsx("div", { id: "roles-profile-list", className: "roles-tree", children: profileListLoading ? _jsx(LoadingState, { label: tr('common.loading') }) : (_jsx(ProfileList, { profiles: filteredProfiles, selectedProfileId: selectedProfileId, tr: tr, onSelect: profileId => void handleSelectProfile(profileId) })) })] }), _jsx("div", { className: "roles-editor-panel", children: !selectedProfileId ? (_jsx("div", { id: "roles-profile-empty", className: "roles-editor-empty", children: tr('roles.profileSelectHint') })) : (_jsxs("div", { id: "roles-profile-detail", className: "roles-editor-form roles-profile-detail", children: [_jsx("div", { className: "roles-profile-title", children: _jsxs("div", { children: [_jsxs("div", { className: "roles-editor-breadcrumb", children: [_jsx("span", { children: selectedProfileId }), selectedProfileBot ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "roles-breadcrumb-sep", children: "\u203A" }), _jsx("span", { children: selectedProfileBot.botName ?? selectedProfileBot.larkAppId })] })) : null] }), _jsx("div", { className: "roles-editor-meta-line", children: tr('roles.profileRuntimeHint') })] }) }), _jsxs("div", { className: "roles-profile-grid", children: [_jsxs("div", { className: "roles-profile-bots", children: [_jsx("div", { className: "roles-profile-section-title", children: tr('roles.profileBots') }), _jsx("div", { className: "roles-profile-bot-list", children: allBots.map(bot => {
449
859
  const hasEntry = !!entryForBot(profileEntries, bot.larkAppId);
450
860
  const selected = selectedProfileBotId === bot.larkAppId;
451
861
  return (_jsxs("div", { className: `roles-bot-row roles-profile-bot-row ${selected ? 'selected' : ''}`, "data-profile-bot-id": bot.larkAppId, onClick: () => void handleSelectProfileBot(bot.larkAppId), children: [_jsx(BotAvatar, { bot: bot }), _jsxs("div", { className: "roles-bot-info", children: [_jsx("div", { className: "roles-bot-name", children: bot.botName ?? bot.larkAppId }), _jsx("div", { className: "roles-bot-id", children: bot.larkAppId })] }), _jsx("span", { className: `roles-badge ${hasEntry ? 'has-role' : 'no-role'}`, children: hasEntry ? tr('roles.configured') : tr('roles.unconfigured') })] }, bot.larkAppId));
@@ -468,6 +878,9 @@ function toggleSet(prev, value) {
468
878
  function byteCountClass(len) {
469
879
  return `roles-bytecount ${len > ROLE_WARN_BYTES ? 'warn' : ''} ${len > MAX_ROLE_BYTES ? 'over' : ''}`;
470
880
  }
881
+ function listenerByteCountClass(len) {
882
+ return `roles-bytecount ${len > MESSAGE_LISTENER_WARN_BYTES ? 'warn' : ''} ${len > MAX_MESSAGE_LISTENER_PROMPT_BYTES ? 'over' : ''}`;
883
+ }
471
884
  function Flash(props) {
472
885
  if (!props.flash)
473
886
  return null;
@@ -487,7 +900,7 @@ function GroupsTree(props) {
487
900
  return (_jsxs("div", { className: `roles-bot-row ${selected ? 'selected' : ''}`, "data-group-id": group.chatId, "data-bot-id": bot.larkAppId, onClick: ev => {
488
901
  ev.stopPropagation();
489
902
  props.onSelectBot(group.chatId, bot.larkAppId);
490
- }, children: [_jsx("span", { className: "roles-bot-indent" }), _jsx(BotAvatar, { bot: bot }), _jsxs("div", { className: "roles-bot-info", children: [_jsx("div", { className: "roles-bot-name", children: bot.botName }), _jsx("div", { className: "roles-bot-id", children: bot.larkAppId })] }), _jsx("span", { className: `roles-badge ${bot.hasRole ? 'has-role' : 'no-role'}`, children: bot.hasRole ? tr('roles.configured') : tr('roles.unconfigured') })] }, bot.larkAppId));
903
+ }, children: [_jsx("span", { className: "roles-bot-indent" }), _jsx(BotAvatar, { bot: bot }), _jsxs("div", { className: "roles-bot-info", children: [_jsx("div", { className: "roles-bot-name", children: bot.botName }), _jsx("div", { className: "roles-bot-id", children: bot.larkAppId })] }), _jsxs("div", { className: "roles-bot-badges", children: [_jsx("span", { className: `roles-badge ${bot.hasRole ? 'has-role' : 'no-role'}`, children: bot.hasRole ? tr('roles.configured') : tr('roles.unconfigured') }), bot.hasMessageListener ? (_jsx("span", { className: "roles-badge has-listener", children: tr('roles.listenerBadge') })) : null] })] }, bot.larkAppId));
491
904
  }) : null })] }, group.chatId));
492
905
  }) }));
493
906
  }
@@ -514,9 +927,121 @@ function GroupProfileStatus(props) {
514
927
  chat: best.chatMatched,
515
928
  }) }));
516
929
  }
930
+ function MessageListenerEditor(props) {
931
+ const { listener, tr } = props;
932
+ const [targetTab, setTargetTab] = useState('members');
933
+ const [targetQuery, setTargetQuery] = useState('');
934
+ const [selectedTargetIds, setSelectedTargetIds] = useState(() => new Set());
935
+ const senderTypes = new Set(listener.senderPolicy?.includeSenderTypes ?? []);
936
+ const msgTypes = new Set(listener.messagePolicy?.includeMsgTypes ?? []);
937
+ const includeIds = new Set(listener.senderPolicy?.includeSenderOpenIds ?? []);
938
+ const configuredUnknownMembers = [
939
+ ...[...includeIds].filter(openId => !props.memberById.has(openId)),
940
+ ];
941
+ const members = [
942
+ ...props.members.filter(member => member.memberType !== 'bot'),
943
+ ...configuredUnknownMembers.map(openId => ({ openId, name: openId, memberType: 'unknown' })),
944
+ ];
945
+ const bots = props.members.filter(member => member.memberType === 'bot');
946
+ const activeTargets = targetTab === 'bots' ? bots : members;
947
+ const filteredTargets = filterListenerTargets(activeTargets, targetQuery);
948
+ const filteredTargetIds = filteredTargets.map(target => target.openId);
949
+ const selectedVisibleIds = filteredTargetIds.filter(id => selectedTargetIds.has(id));
950
+ const allVisibleSelected = filteredTargetIds.length > 0 && selectedVisibleIds.length === filteredTargetIds.length;
951
+ const selectedIds = [...selectedTargetIds].filter(id => activeTargets.some(target => target.openId === id));
952
+ const bulkTargetIds = selectedIds.length > 0 ? selectedIds : filteredTargetIds;
953
+ const bulkState = listenerTargetStateFor({
954
+ include: [...includeIds],
955
+ exclude: [],
956
+ targetIds: bulkTargetIds,
957
+ });
958
+ useEffect(() => {
959
+ setTargetQuery('');
960
+ setSelectedTargetIds(new Set());
961
+ }, [targetTab]);
962
+ useEffect(() => {
963
+ const activeIds = new Set(activeTargets.map(target => target.openId));
964
+ setSelectedTargetIds(prev => {
965
+ const next = new Set([...prev].filter(id => activeIds.has(id)));
966
+ return next.size === prev.size ? prev : next;
967
+ });
968
+ }, [activeTargets]);
969
+ const toggleTargetSelection = (openId, checked) => {
970
+ setSelectedTargetIds(prev => {
971
+ const next = new Set(prev);
972
+ if (checked)
973
+ next.add(openId);
974
+ else
975
+ next.delete(openId);
976
+ return next;
977
+ });
978
+ };
979
+ const toggleAllVisible = () => {
980
+ setSelectedTargetIds(prev => {
981
+ const next = new Set(prev);
982
+ if (allVisibleSelected) {
983
+ for (const id of filteredTargetIds)
984
+ next.delete(id);
985
+ }
986
+ else {
987
+ for (const id of filteredTargetIds)
988
+ next.add(id);
989
+ }
990
+ return next;
991
+ });
992
+ };
993
+ const applyBulkState = (listening) => {
994
+ if (bulkTargetIds.length === 0)
995
+ return;
996
+ props.onSetTargetsPolicy(bulkTargetIds, listening);
997
+ };
998
+ return (_jsxs("div", { className: "roles-listener-editor", children: [props.loading ? _jsx(LoadingState, { label: tr('common.loading') }) : null, _jsxs("label", { className: "filter-toggle roles-listener-enabled", children: [_jsx("input", { type: "checkbox", checked: listener.enabled, onChange: ev => props.onPatch({ enabled: ev.currentTarget.checked }) }), _jsx("span", { className: "filter-toggle-switch", "aria-hidden": "true" }), _jsx("span", { className: "filter-toggle-label", children: tr('roles.listenerEnabled') })] }), _jsx("p", { className: "roles-listener-scope-help", children: tr('roles.listenerScopeHelp') }), _jsxs("div", { className: "roles-listener-grid", children: [_jsxs("label", { className: "roles-listener-field", children: [_jsx("span", { className: "roles-field-label", children: tr('roles.listenerName') }), _jsx("input", { type: "text", value: listener.name ?? '', placeholder: tr('roles.listenerNamePlaceholder'), onChange: ev => props.onPatch({ name: ev.currentTarget.value }) })] }), _jsxs("label", { className: "roles-listener-field", children: [_jsx("span", { className: "roles-field-label", children: tr('roles.listenerReplyCardTitle') }), _jsx("input", { type: "text", value: listener.replyCardTitle ?? '', placeholder: tr('roles.listenerReplyCardTitlePlaceholder'), onChange: ev => props.onPatch({ replyCardTitle: ev.currentTarget.value }) })] }), _jsxs("label", { className: "roles-listener-field", children: [_jsx("span", { className: "roles-field-label", children: tr('roles.listenerWorkingDir') }), _jsx("input", { type: "text", value: listener.workingDir ?? '', placeholder: tr('roles.listenerWorkingDirPlaceholder'), onChange: ev => props.onPatch({ workingDir: ev.currentTarget.value }) })] })] }), _jsxs("div", { className: "roles-listener-policy-row", children: [_jsxs("div", { className: "roles-listener-policy", children: [_jsx("div", { className: "roles-field-label", children: tr('roles.listenerSenderTypes') }), _jsxs("label", { className: "roles-listener-chip", children: [_jsx("input", { type: "checkbox", checked: senderTypes.has('user'), onChange: ev => props.onToggleSenderType('user', ev.currentTarget.checked) }), _jsx("span", { children: tr('roles.listenerSenderUser') })] }), _jsxs("label", { className: "roles-listener-chip", children: [_jsx("input", { type: "checkbox", checked: senderTypes.has('bot'), onChange: ev => props.onToggleSenderType('bot', ev.currentTarget.checked) }), _jsx("span", { children: tr('roles.listenerSenderBot') })] })] }), _jsxs("div", { className: "roles-listener-policy", children: [_jsx("div", { className: "roles-field-label", children: tr('roles.listenerMessageTypes') }), LISTENER_MESSAGE_TYPES.map(msgType => (_jsxs("label", { className: "roles-listener-chip", children: [_jsx("input", { type: "checkbox", checked: msgTypes.has(msgType), onChange: ev => props.onToggleMsgType(msgType, ev.currentTarget.checked) }), _jsx("span", { children: msgType })] }, msgType)))] }), _jsxs("label", { className: "roles-listener-checkbox", children: [_jsx("input", { type: "checkbox", checked: listener.senderPolicy?.excludeSelf !== false, onChange: ev => props.onSenderPolicyPatch({ excludeSelf: ev.currentTarget.checked }) }), _jsx("span", { children: tr('roles.listenerExcludeSelf') })] })] }), _jsxs("label", { className: "roles-listener-field", children: [_jsx("span", { className: "roles-field-label", children: tr('roles.listenerPrompt') }), _jsx("textarea", { id: "roles-listener-prompt", placeholder: tr('roles.listenerPromptPlaceholder'), rows: 9, value: listener.prompt, onChange: ev => props.onPatch({ prompt: ev.currentTarget.value }) })] }), _jsxs("div", { className: "roles-editor-footer", children: [_jsxs("span", { id: "roles-listener-bytecount", className: listenerByteCountClass(props.promptByteLen), children: [props.promptByteLen, " / ", MAX_MESSAGE_LISTENER_PROMPT_BYTES, " bytes"] }), _jsx(Flash, { flash: props.flash })] }), _jsxs("div", { className: "roles-listener-preview-panel", children: [_jsxs("div", { className: "roles-listener-preview-head", children: [_jsxs("div", { children: [_jsx("div", { className: "roles-profile-section-title", children: tr('roles.listenerPreviewTitle') }), _jsx("small", { children: tr('roles.listenerPreviewHint') })] }), _jsxs("div", { className: "roles-listener-preview-actions", children: [_jsxs("label", { children: [_jsx("span", { children: tr('roles.listenerPreviewCount') }), _jsx("select", { value: props.previewLimit, onChange: ev => props.onPreviewLimitChange(Number(ev.currentTarget.value)), children: Array.from({ length: MAX_MESSAGE_LISTENER_PREVIEW_LIMIT }, (_, index) => index + 1).map(count => (_jsx("option", { value: count, children: count }, count))) })] }), _jsx("button", { type: "button", onClick: props.onPreview, disabled: props.previewStatus.kind === 'loading', children: props.previewStatus.kind === 'loading' && props.previewStatus.mode === 'preview' ? '...' : tr('roles.listenerPreviewButton') }), _jsx("button", { type: "button", className: "primary", onClick: props.onRunPreview, disabled: props.previewStatus.kind === 'loading', children: props.previewStatus.kind === 'loading' && props.previewStatus.mode === 'run' ? '...' : tr('roles.listenerRunPreviewButton') })] })] }), _jsx(ListenerPreviewResult, { status: props.previewStatus, tr: tr })] }), _jsxs("div", { className: "roles-listener-members", children: [_jsxs("div", { className: "roles-listener-members-head", children: [_jsx("div", { className: "roles-profile-section-title", children: tr('roles.listenerFilters') }), _jsx("small", { children: props.membersLoading ? tr('common.loading') : tr('roles.listenerMembersHint') })] }), _jsxs("div", { className: "roles-listener-target-tabs segmented", role: "tablist", "aria-label": tr('roles.listenerFilters'), children: [_jsx("button", { type: "button", className: targetTab === 'members' ? 'active' : '', "aria-pressed": targetTab === 'members', onClick: () => setTargetTab('members'), children: tr('roles.listenerMembers') }), _jsx("button", { type: "button", className: targetTab === 'bots' ? 'active' : '', "aria-pressed": targetTab === 'bots', onClick: () => setTargetTab('bots'), children: tr('roles.listenerBots') })] }), _jsxs("div", { className: "roles-listener-filter-toolbar", children: [_jsx("input", { type: "search", value: targetQuery, placeholder: tr('roles.listenerSearchPlaceholder'), onChange: ev => setTargetQuery(ev.currentTarget.value) }), targetQuery ? (_jsx("button", { type: "button", className: "roles-listener-clear-search", onClick: () => setTargetQuery(''), "aria-label": tr('roles.listenerClearSearch'), children: "\u00D7" })) : null, _jsx("button", { type: "button", className: "roles-listener-select-all", disabled: filteredTargetIds.length === 0, onClick: toggleAllVisible, children: tr(allVisibleSelected ? 'roles.listenerClearVisible' : 'roles.listenerSelectVisible', { count: filteredTargetIds.length }) })] }), _jsxs("div", { className: "roles-listener-bulkbar", children: [_jsxs("span", { children: [selectedIds.length > 0
999
+ ? tr('roles.listenerSelectedCount', { count: selectedIds.length })
1000
+ : tr('roles.listenerFilteredCount', { count: filteredTargetIds.length }), bulkState === 'mixed' ? ` · ${tr('roles.listenerMixedMode')}` : ''] }), _jsxs("div", { className: "roles-listener-member-actions", role: "group", "aria-label": tr('roles.listenerBulkActions'), children: [_jsx("button", { type: "button", className: bulkState === 'listen' ? 'active' : '', "aria-pressed": bulkState === 'listen', disabled: bulkTargetIds.length === 0, onClick: () => applyBulkState(true), children: tr('roles.listenerTargetListen') }), _jsx("button", { type: "button", className: bulkState === 'ignore' ? 'active' : '', "aria-pressed": bulkState === 'ignore', disabled: bulkTargetIds.length === 0, onClick: () => applyBulkState(false), children: tr('roles.listenerTargetIgnore') })] })] }), _jsx("div", { className: "roles-listener-member-list", children: filteredTargets.length === 0 ? (_jsx("div", { className: "roles-empty", children: tr(targetTab === 'bots' ? 'roles.listenerBotsEmpty' : 'roles.listenerMembersEmpty') })) : filteredTargets.map(member => {
1001
+ const targetState = listenerTargetStateFor({
1002
+ include: [...includeIds],
1003
+ exclude: [],
1004
+ targetIds: [member.openId],
1005
+ });
1006
+ return (_jsxs("div", { className: "roles-listener-member", children: [_jsx("label", { className: "roles-listener-member-select", "aria-label": memberDisplayName(member, member.openId), children: _jsx("input", { type: "checkbox", checked: selectedTargetIds.has(member.openId), onChange: ev => toggleTargetSelection(member.openId, ev.currentTarget.checked) }) }), _jsxs("div", { className: "roles-listener-member-main", children: [_jsx("strong", { children: memberDisplayName(member, member.openId) }), _jsx("span", { children: member.openId })] }), _jsxs("div", { className: "roles-listener-member-actions", role: "group", "aria-label": memberDisplayName(member, member.openId), children: [_jsx("button", { type: "button", className: targetState === 'listen' ? 'active' : '', "aria-pressed": targetState === 'listen', onClick: () => props.onSetTargetPolicy(member.openId, true), children: tr('roles.listenerTargetListen') }), _jsx("button", { type: "button", className: targetState === 'ignore' ? 'active' : '', "aria-pressed": targetState === 'ignore', onClick: () => props.onSetTargetPolicy(member.openId, false), children: tr('roles.listenerTargetIgnore') })] })] }, member.openId));
1007
+ }) })] })] }));
1008
+ }
517
1009
  function RolePreview(props) {
518
1010
  return (_jsx("div", { id: props.id, className: "roles-preview", children: props.content.trim() ? (_jsxs(_Fragment, { children: [_jsx("strong", { children: props.tr('roles.preview') }), _jsx("pre", { children: props.content })] })) : (_jsx("small", { children: props.tr('roles.previewEmpty') })) }));
519
1011
  }
1012
+ function ListenerPreviewResult(props) {
1013
+ const { status, tr } = props;
1014
+ if (status.kind === 'idle')
1015
+ return _jsx("div", { className: "roles-listener-preview-empty", children: tr('roles.listenerPreviewEmpty') });
1016
+ if (status.kind === 'loading')
1017
+ return _jsx(LoadingState, { label: tr('common.loading') });
1018
+ if (status.kind === 'error')
1019
+ return _jsx("div", { className: "roles-listener-preview-error", children: status.text });
1020
+ const matches = status.response.matches ?? [];
1021
+ const results = status.response.results ?? [];
1022
+ const stateCounts = results.reduce((acc, result) => {
1023
+ const state = !result.ok ? 'failed' : result.state ?? 'triggered';
1024
+ acc[state] = (acc[state] ?? 0) + 1;
1025
+ return acc;
1026
+ }, {});
1027
+ return (_jsxs("div", { className: "roles-listener-preview-results", children: [_jsx("div", { className: "roles-listener-preview-summary", children: status.mode === 'run'
1028
+ ? tr('roles.listenerRunPreviewSummary', {
1029
+ count: matches.length,
1030
+ triggered: stateCounts.triggered ?? 0,
1031
+ running: stateCounts.running ?? 0,
1032
+ replied: stateCounts.replied ?? 0,
1033
+ failed: stateCounts.failed ?? 0,
1034
+ })
1035
+ : tr('roles.listenerPreviewSummary', { count: matches.length }) }), matches.length === 0 ? (_jsx("div", { className: "roles-listener-preview-empty", children: tr('roles.listenerPreviewNoMatches') })) : (_jsx("div", { className: "roles-listener-preview-list", children: matches.map(item => (_jsx(ListenerPreviewItem, { item: item, result: status.response.results?.find(result => result.messageId === item.messageId), tr: tr }, item.messageId))) }))] }));
1036
+ }
1037
+ function ListenerPreviewItem(props) {
1038
+ const text = props.item.messageText.length > 300
1039
+ ? `${props.item.messageText.slice(0, 300)}...`
1040
+ : props.item.messageText;
1041
+ const senderLabel = props.item.senderName || props.item.senderOpenId || props.item.senderType;
1042
+ const sentAt = formatListenerPreviewTime(props.item.createTime);
1043
+ return (_jsxs("div", { className: "roles-listener-preview-item", children: [_jsxs("div", { className: "roles-listener-preview-meta", children: [_jsx("span", { children: props.item.msgType }), _jsx("span", { children: props.item.senderType }), props.item.senderOpenId ? _jsx("span", { children: props.item.senderOpenId }) : null, _jsx("span", { children: props.item.messageId }), props.result ? (_jsx("span", { className: listenerRunPreviewStateClass(props.result.state, props.result.ok), children: props.tr(`roles.listenerRunPreviewState.${!props.result.ok ? 'failed' : props.result.state ?? 'triggered'}`) })) : null] }), props.result?.sessionId || props.result?.replyMessageId || props.result?.error ? (_jsxs("div", { className: "roles-listener-preview-run-detail", children: [props.result.sessionId ? _jsx("span", { children: props.result.sessionId }) : null, props.result.replyMessageId ? _jsx("span", { children: props.result.replyMessageId }) : null, props.result.error ? _jsx("strong", { children: props.result.error }) : null] })) : null, _jsxs("div", { className: "roles-listener-preview-sender", children: [_jsx("span", { children: props.tr('roles.listenerPreviewSender') }), _jsx("strong", { children: senderLabel })] }), sentAt ? (_jsxs("div", { className: "roles-listener-preview-time", children: [_jsx("span", { children: props.tr('roles.listenerPreviewTime') }), _jsx("strong", { children: sentAt })] })) : null, props.item.messageTitle ? (_jsxs("div", { className: "roles-listener-preview-title", children: [_jsx("span", { children: props.tr('roles.listenerPreviewMessageTitle') }), _jsx("strong", { children: props.item.messageTitle })] })) : null, _jsx("div", { className: "roles-listener-preview-content-label", children: props.tr('roles.listenerPreviewContent') }), _jsx("pre", { children: text })] }));
1044
+ }
520
1045
  function ProfileList(props) {
521
1046
  const { tr } = props;
522
1047
  if (props.profiles.length === 0)