botmux 3.0.0 → 3.1.1

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 (220) hide show
  1. package/dist/adapters/backend/herdr-backend.d.ts +9 -7
  2. package/dist/adapters/backend/herdr-backend.d.ts.map +1 -1
  3. package/dist/adapters/backend/herdr-backend.js +63 -53
  4. package/dist/adapters/backend/herdr-backend.js.map +1 -1
  5. package/dist/adapters/backend/riff-backend.d.ts +6 -1
  6. package/dist/adapters/backend/riff-backend.d.ts.map +1 -1
  7. package/dist/adapters/backend/riff-backend.js +7 -2
  8. package/dist/adapters/backend/riff-backend.js.map +1 -1
  9. package/dist/adapters/backend/sandbox.d.ts +20 -16
  10. package/dist/adapters/backend/sandbox.d.ts.map +1 -1
  11. package/dist/adapters/backend/sandbox.js +193 -68
  12. package/dist/adapters/backend/sandbox.js.map +1 -1
  13. package/dist/adapters/backend/tmux-backend.d.ts +39 -2
  14. package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
  15. package/dist/adapters/backend/tmux-backend.js +66 -6
  16. package/dist/adapters/backend/tmux-backend.js.map +1 -1
  17. package/dist/adapters/backend/tmux-pipe-backend.js +2 -2
  18. package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
  19. package/dist/adapters/backend/zellij-backend.d.ts.map +1 -1
  20. package/dist/adapters/backend/zellij-backend.js +9 -3
  21. package/dist/adapters/backend/zellij-backend.js.map +1 -1
  22. package/dist/adapters/cli/claude-code.d.ts +25 -0
  23. package/dist/adapters/cli/claude-code.d.ts.map +1 -1
  24. package/dist/adapters/cli/claude-code.js +79 -9
  25. package/dist/adapters/cli/claude-code.js.map +1 -1
  26. package/dist/adapters/cli/read-isolation.d.ts +2 -1
  27. package/dist/adapters/cli/read-isolation.d.ts.map +1 -1
  28. package/dist/adapters/cli/read-isolation.js.map +1 -1
  29. package/dist/adapters/cli/types.d.ts +2 -2
  30. package/dist/adapters/hook-installer.d.ts +11 -0
  31. package/dist/adapters/hook-installer.d.ts.map +1 -1
  32. package/dist/adapters/hook-installer.js +72 -11
  33. package/dist/adapters/hook-installer.js.map +1 -1
  34. package/dist/cli.d.ts.map +1 -1
  35. package/dist/cli.js +15 -4
  36. package/dist/cli.js.map +1 -1
  37. package/dist/config.d.ts +6 -0
  38. package/dist/config.d.ts.map +1 -1
  39. package/dist/config.js +18 -0
  40. package/dist/config.js.map +1 -1
  41. package/dist/core/command-handler.d.ts.map +1 -1
  42. package/dist/core/command-handler.js +354 -143
  43. package/dist/core/command-handler.js.map +1 -1
  44. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  45. package/dist/core/dashboard-ipc-server.js +12 -5
  46. package/dist/core/dashboard-ipc-server.js.map +1 -1
  47. package/dist/core/dashboard-rows.d.ts +5 -0
  48. package/dist/core/dashboard-rows.d.ts.map +1 -1
  49. package/dist/core/dashboard-rows.js +2 -0
  50. package/dist/core/dashboard-rows.js.map +1 -1
  51. package/dist/core/doc-comment-prompt.d.ts +2 -0
  52. package/dist/core/doc-comment-prompt.d.ts.map +1 -1
  53. package/dist/core/doc-comment-prompt.js +30 -0
  54. package/dist/core/doc-comment-prompt.js.map +1 -1
  55. package/dist/core/github-auth.d.ts +8 -0
  56. package/dist/core/github-auth.d.ts.map +1 -0
  57. package/dist/core/github-auth.js +44 -0
  58. package/dist/core/github-auth.js.map +1 -0
  59. package/dist/core/managed-origin-capability.d.ts +7 -0
  60. package/dist/core/managed-origin-capability.d.ts.map +1 -1
  61. package/dist/core/managed-origin-capability.js +12 -0
  62. package/dist/core/managed-origin-capability.js.map +1 -1
  63. package/dist/core/passthrough-commands.d.ts.map +1 -1
  64. package/dist/core/passthrough-commands.js +1 -1
  65. package/dist/core/passthrough-commands.js.map +1 -1
  66. package/dist/core/restart-report.d.ts +7 -1
  67. package/dist/core/restart-report.d.ts.map +1 -1
  68. package/dist/core/restart-report.js +19 -5
  69. package/dist/core/restart-report.js.map +1 -1
  70. package/dist/core/session-activity.d.ts +8 -0
  71. package/dist/core/session-activity.d.ts.map +1 -1
  72. package/dist/core/session-activity.js +16 -0
  73. package/dist/core/session-activity.js.map +1 -1
  74. package/dist/core/session-discovery.d.ts.map +1 -1
  75. package/dist/core/session-discovery.js +4 -2
  76. package/dist/core/session-discovery.js.map +1 -1
  77. package/dist/core/session-marker.d.ts.map +1 -1
  78. package/dist/core/session-marker.js +5 -2
  79. package/dist/core/session-marker.js.map +1 -1
  80. package/dist/core/types.d.ts +48 -0
  81. package/dist/core/types.d.ts.map +1 -1
  82. package/dist/core/types.js +55 -0
  83. package/dist/core/types.js.map +1 -1
  84. package/dist/core/update-check.d.ts +11 -0
  85. package/dist/core/update-check.d.ts.map +1 -1
  86. package/dist/core/update-check.js +6 -1
  87. package/dist/core/update-check.js.map +1 -1
  88. package/dist/core/vc-meeting-prepare-command.d.ts +22 -0
  89. package/dist/core/vc-meeting-prepare-command.d.ts.map +1 -0
  90. package/dist/core/vc-meeting-prepare-command.js +50 -0
  91. package/dist/core/vc-meeting-prepare-command.js.map +1 -0
  92. package/dist/core/worker-pool.d.ts.map +1 -1
  93. package/dist/core/worker-pool.js +21 -10
  94. package/dist/core/worker-pool.js.map +1 -1
  95. package/dist/daemon.d.ts +15 -5
  96. package/dist/daemon.d.ts.map +1 -1
  97. package/dist/daemon.js +645 -50
  98. package/dist/daemon.js.map +1 -1
  99. package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -1
  100. package/dist/dashboard/web/bot-defaults-page.js +6 -1
  101. package/dist/dashboard/web/bot-defaults-page.js.map +1 -1
  102. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  103. package/dist/dashboard/web/i18n.js +34 -0
  104. package/dist/dashboard/web/i18n.js.map +1 -1
  105. package/dist/dashboard/web/preferences.d.ts +1 -1
  106. package/dist/dashboard/web/preferences.d.ts.map +1 -1
  107. package/dist/dashboard/web/preferences.js +1 -1
  108. package/dist/dashboard/web/preferences.js.map +1 -1
  109. package/dist/dashboard/web/sessions-page.d.ts +19 -0
  110. package/dist/dashboard/web/sessions-page.d.ts.map +1 -1
  111. package/dist/dashboard/web/sessions-page.js +66 -7
  112. package/dist/dashboard/web/sessions-page.js.map +1 -1
  113. package/dist/dashboard/web/sessions.d.ts +31 -0
  114. package/dist/dashboard/web/sessions.d.ts.map +1 -1
  115. package/dist/dashboard/web/sessions.js +82 -0
  116. package/dist/dashboard/web/sessions.js.map +1 -1
  117. package/dist/dashboard-web/app.js +1 -1
  118. package/dist/dashboard-web/chunks/{bot-defaults-page-MBORWWP7.js → bot-defaults-page-KLDCZNLP.js} +5 -5
  119. package/dist/dashboard-web/chunks/{chunk-G43RUPUN.js → chunk-EZAW2EPM.js} +1 -1
  120. package/dist/dashboard-web/chunks/{chunk-EIO5AWDA.js → chunk-FMJNOATE.js} +1 -1
  121. package/dist/dashboard-web/chunks/{chunk-4QTBR7UF.js → chunk-KEAOTUL3.js} +7 -7
  122. package/dist/dashboard-web/chunks/{chunk-7STGZ5BX.js → chunk-MLNI5BKE.js} +1 -1
  123. package/dist/dashboard-web/chunks/{connectors-page-LKDT4WBG.js → connectors-page-O32LFAL5.js} +1 -1
  124. package/dist/dashboard-web/chunks/{groups-page-XKIWJ2TP.js → groups-page-PSAFIZ6M.js} +1 -1
  125. package/dist/dashboard-web/chunks/{insights-page-CBEZCYKV.js → insights-page-V23LLATX.js} +1 -1
  126. package/dist/dashboard-web/chunks/{monitor-room-AQTHAWXA.js → monitor-room-7X5AKYOF.js} +1 -1
  127. package/dist/dashboard-web/chunks/{monitoring-page-2R4NCNBQ.js → monitoring-page-CBA3CQYL.js} +1 -1
  128. package/dist/dashboard-web/chunks/{office-page-5M6MQIIL.js → office-page-RL6QSEM7.js} +1 -1
  129. package/dist/dashboard-web/chunks/{overview-page-N22HDZ2V.js → overview-page-XQIPO6KH.js} +1 -1
  130. package/dist/dashboard-web/chunks/{roles-page-J6K2HWFX.js → roles-page-ACIWV7QS.js} +1 -1
  131. package/dist/dashboard-web/chunks/{schedules-page-CGAQCB4J.js → schedules-page-KJDUKOJV.js} +1 -1
  132. package/dist/dashboard-web/chunks/sessions-page-2WDWHSP4.js +3 -0
  133. package/dist/dashboard-web/chunks/{settings-page-GLRWV5KU.js → settings-page-6VX4OUGG.js} +1 -1
  134. package/dist/dashboard-web/chunks/{skills-page-KQVW42QU.js → skills-page-WXE3UYLT.js} +1 -1
  135. package/dist/dashboard-web/chunks/{team-federation-page-5PYUOV5M.js → team-federation-page-PYTF4FLM.js} +1 -1
  136. package/dist/dashboard-web/chunks/{v3-page-DXPFILRZ.js → v3-page-G6CD6O3I.js} +1 -1
  137. package/dist/dashboard-web/chunks/{whiteboards-page-NUH6VZ4I.js → whiteboards-page-6SG33GP4.js} +1 -1
  138. package/dist/dashboard-web/style.css +167 -2
  139. package/dist/i18n/en.d.ts.map +1 -1
  140. package/dist/i18n/en.js +19 -3
  141. package/dist/i18n/en.js.map +1 -1
  142. package/dist/i18n/zh.d.ts.map +1 -1
  143. package/dist/i18n/zh.js +19 -3
  144. package/dist/i18n/zh.js.map +1 -1
  145. package/dist/im/lark/card-builder.d.ts.map +1 -1
  146. package/dist/im/lark/card-builder.js +11 -2
  147. package/dist/im/lark/card-builder.js.map +1 -1
  148. package/dist/im/lark/card-handler.d.ts +2 -0
  149. package/dist/im/lark/card-handler.d.ts.map +1 -1
  150. package/dist/im/lark/card-handler.js +219 -139
  151. package/dist/im/lark/card-handler.js.map +1 -1
  152. package/dist/im/lark/event-dispatcher.d.ts +4 -0
  153. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  154. package/dist/im/lark/event-dispatcher.js +249 -5
  155. package/dist/im/lark/event-dispatcher.js.map +1 -1
  156. package/dist/im/lark/forward-followup-buffer.d.ts +26 -0
  157. package/dist/im/lark/forward-followup-buffer.d.ts.map +1 -0
  158. package/dist/im/lark/forward-followup-buffer.js +48 -0
  159. package/dist/im/lark/forward-followup-buffer.js.map +1 -0
  160. package/dist/im/lark/forward-followup-content.d.ts +6 -0
  161. package/dist/im/lark/forward-followup-content.d.ts.map +1 -0
  162. package/dist/im/lark/forward-followup-content.js +46 -0
  163. package/dist/im/lark/forward-followup-content.js.map +1 -0
  164. package/dist/im/lark/forward-followup-store.d.ts +9 -0
  165. package/dist/im/lark/forward-followup-store.d.ts.map +1 -0
  166. package/dist/im/lark/forward-followup-store.js +48 -0
  167. package/dist/im/lark/forward-followup-store.js.map +1 -0
  168. package/dist/im/lark/message-parser.d.ts +1 -1
  169. package/dist/im/lark/message-parser.d.ts.map +1 -1
  170. package/dist/im/lark/message-parser.js +1 -2
  171. package/dist/im/lark/message-parser.js.map +1 -1
  172. package/dist/platform/tunnel-client.js +3 -3
  173. package/dist/platform/tunnel-client.js.map +1 -1
  174. package/dist/services/local-cli-opener.d.ts +7 -0
  175. package/dist/services/local-cli-opener.d.ts.map +1 -1
  176. package/dist/services/local-cli-opener.js +111 -3
  177. package/dist/services/local-cli-opener.js.map +1 -1
  178. package/dist/services/skill-registry-store.d.ts.map +1 -1
  179. package/dist/services/skill-registry-store.js +15 -2
  180. package/dist/services/skill-registry-store.js.map +1 -1
  181. package/dist/services/vc-meeting-consumer-isolation.d.ts +7 -5
  182. package/dist/services/vc-meeting-consumer-isolation.d.ts.map +1 -1
  183. package/dist/services/vc-meeting-consumer-isolation.js +7 -5
  184. package/dist/services/vc-meeting-consumer-isolation.js.map +1 -1
  185. package/dist/services/vc-meeting-preparations-store.d.ts +22 -0
  186. package/dist/services/vc-meeting-preparations-store.d.ts.map +1 -0
  187. package/dist/services/vc-meeting-preparations-store.js +131 -0
  188. package/dist/services/vc-meeting-preparations-store.js.map +1 -0
  189. package/dist/services/vc-meeting-runtime-store.d.ts +9 -0
  190. package/dist/services/vc-meeting-runtime-store.d.ts.map +1 -1
  191. package/dist/services/vc-meeting-runtime-store.js +16 -0
  192. package/dist/services/vc-meeting-runtime-store.js.map +1 -1
  193. package/dist/setup/ensure-tmux.d.ts.map +1 -1
  194. package/dist/setup/ensure-tmux.js +9 -11
  195. package/dist/setup/ensure-tmux.js.map +1 -1
  196. package/dist/types.d.ts +2 -0
  197. package/dist/types.d.ts.map +1 -1
  198. package/dist/utils/child-env.d.ts +24 -8
  199. package/dist/utils/child-env.d.ts.map +1 -1
  200. package/dist/utils/child-env.js +49 -8
  201. package/dist/utils/child-env.js.map +1 -1
  202. package/dist/utils/daemon-discovery.d.ts +8 -0
  203. package/dist/utils/daemon-discovery.d.ts.map +1 -1
  204. package/dist/utils/daemon-discovery.js +19 -0
  205. package/dist/utils/daemon-discovery.js.map +1 -1
  206. package/dist/utils/ready-gate.d.ts +9 -3
  207. package/dist/utils/ready-gate.d.ts.map +1 -1
  208. package/dist/utils/ready-gate.js +12 -4
  209. package/dist/utils/ready-gate.js.map +1 -1
  210. package/dist/worker.js +141 -29
  211. package/dist/worker.js.map +1 -1
  212. package/dist/workflows/ops-projection.d.ts.map +1 -1
  213. package/dist/workflows/ops-projection.js +27 -5
  214. package/dist/workflows/ops-projection.js.map +1 -1
  215. package/dist/workflows/v3/distillation-runner.d.ts +4 -0
  216. package/dist/workflows/v3/distillation-runner.d.ts.map +1 -1
  217. package/dist/workflows/v3/distillation-runner.js +4 -2
  218. package/dist/workflows/v3/distillation-runner.js.map +1 -1
  219. package/package.json +1 -1
  220. package/dist/dashboard-web/chunks/sessions-page-WJ2QYTIE.js +0 -3
@@ -6,11 +6,11 @@ import { mountReactPage } from './react-mount.js';
6
6
  import { useStoreSelector, useT } from './react-hooks.js';
7
7
  import { KANBAN_TEAM_STORAGE_KEY, normalizeSessionsViewMode, readStoredBoardOrder, readStoredCreateKeepOpen, readStoredKanbanGroupBy, readStoredSessionsShowUnknownChats, readStoredSessionsViewMode, writeStoredBoardOrder, writeStoredCreateKeepOpen, writeStoredKanbanGroupBy, writeStoredSessionsShowUnknownChats, writeStoredSessionsViewMode, } from './preferences.js';
8
8
  import { OPEN_CREATE_SESSION_EVENT, consumePendingCreateSession } from './create-session-entry.js';
9
- import { BOARD_COLUMNS, CLI_FILTER_OPTIONS, ICON, SESSION_STATUS_OPTIONS, canRestartSession, cssToken, deriveSessionBoardColumn, fetchPickerBots, formatTokenCount, historySenderKey, isUnknownChatSession, lockActionLabel, openWriteLink, repoBasename, restartConfirmMessage, sessionLocationText, sessionLocationTitle, sessionRuntimeCounts, sessionSearchText, sessionStatusText, shouldOpenWritableTerminal, terminalHref, tokenCount, } from './sessions.js';
9
+ import { BOARD_COLUMNS, CLI_FILTER_OPTIONS, ICON, SESSION_STATUS_OPTIONS, canRestartSession, cssToken, deriveSessionBoardColumn, fetchPickerBots, formatTokenCount, groupSessionsByTopic, historySenderKey, isUnknownChatSession, lockActionLabel, openWriteLink, repoBasename, restartConfirmMessage, sessionLocationText, sessionLocationTitle, sessionRuntimeCounts, sessionSearchText, sessionTopicKey, sessionStatusText, shouldOpenWritableTerminal, terminalHref, tokenCount, } from './sessions.js';
10
10
  import { addMonitorRoomSessionIds, monitorRoomUrl } from './monitor-room-store.js';
11
11
  import { CreateActionButton, DropdownMenu, LoadingState } from './dashboard-components.js';
12
12
  import { store } from './store.js';
13
- import { attentionWaitSince, botAvatarHtml, botDisplayName, chatDisplayTitle, loadNameMaps, relTime, stripMentionPrefix, t, ui, } from './ui.js';
13
+ import { attentionWaitSince, botAvatarHtml, botDisplayName, chatAvatarHtml, chatDisplayTitle, loadNameMaps, relTime, stripMentionPrefix, t, ui, } from './ui.js';
14
14
  import { SessionsKanbanView, } from './sessions-kanban.js';
15
15
  function idleCleanupHoursLabel(hours) {
16
16
  return hours === 168 ? '7d' : `${hours}H`;
@@ -240,7 +240,13 @@ function SessionsFilters(props) {
240
240
  next.delete(cli);
241
241
  return { ...prev, cli: next };
242
242
  });
243
- } }), _jsxs("label", { className: "filter-toggle", children: [_jsx("input", { type: "checkbox", name: "showUnknownChats", checked: props.filters.showUnknownChats, onChange: event => {
243
+ } }), _jsxs("label", { className: "filter-toggle", children: [_jsx("input", { type: "checkbox", name: "multiBotTopics", checked: props.filters.multiBotTopics, onChange: event => {
244
+ const multiBotTopics = event.currentTarget.checked;
245
+ props.setFilters(prev => ({ ...prev, multiBotTopics }));
246
+ } }), _jsx("span", { className: "filter-toggle-label", children: t('sessions.multiBotTopics') }), _jsx("span", { className: "filter-toggle-switch", "aria-hidden": "true" })] }), _jsxs("label", { className: "filter-toggle", children: [_jsx("input", { type: "checkbox", name: "botTriggeredTopics", checked: props.filters.botTriggeredTopics, onChange: event => {
247
+ const botTriggeredTopics = event.currentTarget.checked;
248
+ props.setFilters(prev => ({ ...prev, botTriggeredTopics }));
249
+ } }), _jsx("span", { className: "filter-toggle-label", children: t('sessions.botTriggeredTopics') }), _jsx("span", { className: "filter-toggle-switch", "aria-hidden": "true" })] }), _jsxs("label", { className: "filter-toggle", children: [_jsx("input", { type: "checkbox", name: "showUnknownChats", checked: props.filters.showUnknownChats, onChange: event => {
244
250
  const checked = event.currentTarget.checked;
245
251
  writeStoredSessionsShowUnknownChats(windowStorage(), checked);
246
252
  props.setFilters(prev => ({ ...prev, showUnknownChats: checked }));
@@ -388,7 +394,7 @@ function BoardCard(props) {
388
394
  };
389
395
  return (_jsxs("article", { className: `session-card${props.selected ? ' selected' : ''}${row.locked ? ' locked' : ''}`, "data-id": row.sessionId, "aria-pressed": props.selected, onClick: onCardClick, children: [_jsxs("div", { className: "session-card-top", children: [_jsx("span", { dangerouslySetInnerHTML: rawHtml(botAvatarHtml({ name: botName, larkAppId: row.larkAppId, size: 'sm' })) }), _jsxs("div", { className: "session-card-title", children: [_jsx("strong", { title: String(row.title ?? title), children: String(title).slice(0, 72) }), _jsxs("span", { children: [botName, " \u00B7 ", chatTitle ?? row.cliId ?? 'unknown'] })] }), _jsxs("span", { className: "session-card-status-group", children: [_jsx(StatusBadge, { status: row.status }), _jsx(LockChip, { row: row })] })] }), repo !== '-' || row.adopt || signal ? (_jsxs("div", { className: "session-card-meta", children: [repo !== '-' ? _jsx("span", { title: row.workingDir ?? '', children: repo }) : null, row.adopt ? _jsx("span", { className: "badge", children: "adopt" }) : null, signal ? _jsx("span", { className: "session-signal", title: signal, children: signal }) : null] })) : null, _jsx("div", { className: "session-card-time", children: _jsx("span", { children: row.agentAttention?.at
390
396
  ? `${t('sessions.board.waiting')} ${relTime(attentionWaitSince(row))}`
391
- : `${t('sessions.last')}: ${relTime(row.lastMessageAt)}` }) }), _jsxs("div", { className: "session-card-actions", children: [_jsx(ChatScopeLink, { row: row }), !row.feishuChatLink || row.scope !== 'chat' ? (_jsx(LocateIconButton, { row: row, onLocate: props.onLocate })) : null, _jsx(IconActionButton, { action: "details", icon: ICON.details, label: t('sessions.details'), onClick: () => props.onOpen(row) }), canRestartSession(row) ? (_jsx(IconActionButton, { action: "restart", icon: ICON.restart, label: t('sessions.restart'), onClick: button => props.onRestart(row, button) })) : null, _jsx(TerminalControls, { row: row, url: term }), _jsx(IconActionButton, { action: "lock", icon: row.locked ? ICON.unlock : ICON.lock, label: lockActionLabel(row), kind: row.locked ? 'locked' : '', onClick: button => props.onLock(row, !row.locked, button) }), _jsx(IconActionButton, { action: "close", icon: ICON.close, label: t('sessions.close'), kind: "danger", onClick: button => props.onClose(row, button) })] })] }));
397
+ : `${t('sessions.last')}: ${relTime(row.lastMessageAt)}` }) }), _jsxs("div", { className: "session-card-actions", children: [_jsx(ChatScopeLink, { row: row }), !row.feishuChatLink || row.scope !== 'chat' ? (_jsx(LocateIconButton, { row: row, onLocate: props.onLocate })) : null, _jsx(IconActionButton, { action: "details", icon: ICON.details, label: t('sessions.details'), onClick: () => props.onOpen(row) }), canRestartSession(row) ? (_jsx(IconActionButton, { action: "restart", icon: ICON.restart, label: t('sessions.restart'), onClick: button => props.onRestart(row, button) })) : null, _jsx(TerminalControls, { row: row, url: term }), row.status !== 'closed' ? (_jsxs(_Fragment, { children: [_jsx(IconActionButton, { action: "lock", icon: row.locked ? ICON.unlock : ICON.lock, label: lockActionLabel(row), kind: row.locked ? 'locked' : '', onClick: button => props.onLock(row, !row.locked, button) }), _jsx(IconActionButton, { action: "close", icon: ICON.close, label: t('sessions.close'), kind: "danger", onClick: button => props.onClose(row, button) })] })) : null] })] }));
392
398
  }
393
399
  function BoardView(props) {
394
400
  useEffect(() => {
@@ -433,6 +439,38 @@ function BoardView(props) {
433
439
  }, children: [_jsxs("div", { children: [_jsx("h2", { children: t(column.labelKey) }), _jsx("p", { children: t(column.hintKey) })] }), _jsxs("span", { className: "session-board-head-right", children: [_jsxs("span", { className: "session-board-move", children: [_jsx("button", { type: "button", "data-move-col": column.id, "data-dir": "-1", "aria-label": t('sessions.board.moveLeft'), disabled: idx === 0, onClick: () => props.onMoveColumn(column.id, -1), children: "\u2039" }), _jsx("button", { type: "button", "data-move-col": column.id, "data-dir": "1", "aria-label": t('sessions.board.moveRight'), disabled: idx === columns.length - 1, onClick: () => props.onMoveColumn(column.id, 1), children: "\u203A" })] }), _jsx("span", { className: "session-board-count", children: columnRows.length })] })] }), _jsx("div", { className: "session-board-list", children: columnRows.length ? columnRows.map(row => (_jsx(BoardCard, { row: row, selected: props.selected.has(row.sessionId), onToggleSelect: props.onToggleSelect, onOpen: props.onOpen, onHistory: props.onHistory, onLocate: props.onLocate, onRestart: props.onRestart, onLock: props.onLock, onClose: props.onClose }, row.sessionId))) : _jsx("div", { className: "session-board-empty", children: t('sessions.board.emptyColumn') }) })] }, column.id));
434
440
  }) }));
435
441
  }
442
+ function topicGroupTitle(group) {
443
+ const title = stripMentionPrefix(group.title).trim();
444
+ if (title)
445
+ return title;
446
+ if (group.kind === 'thread')
447
+ return t('sessions.topic.untitled');
448
+ return group.kind === 'chat' ? t('sessions.topic.wholeChat') : t('sessions.topic.singleSession');
449
+ }
450
+ export function TopicGroupsView(props) {
451
+ const groups = useMemo(() => groupSessionsByTopic(props.rows), [props.rows]);
452
+ const relationGroups = useMemo(() => new Map(groupSessionsByTopic(props.relationRows ?? props.rows).map(group => [group.key, group])), [props.relationRows, props.rows]);
453
+ return (_jsx("div", { id: "sessions-topics", className: "sessions-topic-view", hidden: props.hidden, children: groups.length ? groups.map(group => {
454
+ // Member cards respect the current session filters. Header metadata is
455
+ // derived from the complete store so a hidden closed sibling does not
456
+ // erase the topic's multi-Bot relationship.
457
+ const relation = relationGroups.get(group.key) ?? group;
458
+ const representative = group.rows[0];
459
+ const location = sessionLocationText(representative);
460
+ const title = topicGroupTitle(relation);
461
+ const anchor = relation.rootMessageId || relation.chatId;
462
+ return (_jsxs("section", { className: `session-topic-group${relation.multiBot ? ' multi-bot' : ''}${relation.inferredBotTriggered ? ' inferred-bot-trigger' : ''}`, "data-topic-key": group.key, children: [_jsxs("header", { children: [_jsx("span", { className: "session-topic-avatar", dangerouslySetInnerHTML: rawHtml(chatAvatarHtml({ chatId: group.chatId, name: location, size: 'sm' })) }), _jsxs("div", { className: "session-topic-heading", children: [_jsx("span", { className: "session-topic-location", children: location }), _jsx("h2", { title: relation.title, children: title }), _jsx("code", { title: anchor, children: relation.kind === 'thread'
463
+ ? anchor
464
+ : relation.kind === 'chat'
465
+ ? t('sessions.topic.wholeChat')
466
+ : t('sessions.topic.singleSession') })] }), _jsxs("div", { className: "session-topic-summary", "aria-label": t('sessions.topic.summary'), children: [relation.multiBot ? _jsx("span", { className: "topic-chip collaboration", children: t('sessions.topic.collaboration') }) : null, _jsx("span", { className: "topic-chip", children: t('sessions.topic.sessions', { count: relation.rows.length }) }), _jsx("span", { className: "topic-chip", children: t('sessions.topic.bots', { count: relation.botCount }) }), _jsx("span", { className: "topic-chip active", children: t('sessions.topic.active', { count: relation.activeCount }) }), relation.closedCount ? _jsx("span", { className: "topic-chip", children: t('sessions.topic.closed', { count: relation.closedCount }) }) : null, relation.inferredBotInputCount ? (_jsx("span", { className: "topic-chip inferred", title: t('sessions.topic.inferredHint'), children: t('sessions.topic.botInputs', { count: relation.inferredBotInputCount }) })) : null, _jsxs("span", { className: "session-topic-updated", children: [t('sessions.last'), ": ", relTime(relation.latestActivityAt)] })] })] }), _jsx("div", { className: "session-topic-members", children: group.rows.map(row => (_jsx(BoardCard, { row: row, selected: props.selected.has(row.sessionId), onToggleSelect: candidate => {
467
+ if (candidate.status !== 'closed')
468
+ props.onToggleSelect(candidate);
469
+ else
470
+ props.onOpen(candidate);
471
+ }, onOpen: props.onOpen, onHistory: props.onHistory, onLocate: props.onLocate, onRestart: props.onRestart, onLock: props.onLock, onClose: props.onClose }, row.sessionId))) })] }, group.key));
472
+ }) : _jsx("div", { className: "sessions-topic-empty", children: t('sessions.topic.empty') }) }));
473
+ }
436
474
  function HistoryBubble(props) {
437
475
  const m = props.message;
438
476
  const human = m.senderType === 'user';
@@ -797,6 +835,8 @@ function SessionsPage() {
797
835
  status: '',
798
836
  adopt: '',
799
837
  chat: '',
838
+ multiBotTopics: false,
839
+ botTriggeredTopics: false,
800
840
  showUnknownChats: readStoredSessionsShowUnknownChats(windowStorage()),
801
841
  active: true,
802
842
  cli: new Set(CLI_FILTER_OPTIONS),
@@ -885,20 +925,31 @@ function SessionsPage() {
885
925
  const cli = [...filters.cli];
886
926
  const cliFilterActive = cli.length > 0 && cli.length < CLI_FILTER_OPTIONS.length;
887
927
  const keepClosed = viewMode === 'kanban';
888
- return storeRows
928
+ const acceptedTopicKeys = filters.multiBotTopics || filters.botTriggeredTopics
929
+ ? new Set(groupSessionsByTopic(storeRows)
930
+ .filter(group => !filters.multiBotTopics || group.multiBot)
931
+ .filter(group => !filters.botTriggeredTopics || group.inferredBotTriggered)
932
+ .map(group => group.key))
933
+ : null;
934
+ const base = storeRows
889
935
  .filter(s => !cliFilterActive || cli.includes(s.cliId ?? 'unknown'))
890
936
  .filter(s => filters.showUnknownChats || !isUnknownChatSession(s))
891
937
  .filter(s => !filters.status || s.status === filters.status)
892
938
  .filter(s => !filters.adopt || (filters.adopt === 'yes') === !!s.adopt)
893
939
  .filter(s => !filters.chat || String(s.chatId ?? '') === filters.chat)
894
940
  .filter(s => !filters.active || keepClosed || s.status !== 'closed')
895
- .filter(s => !q || sessionSearchText(s).includes(q))
941
+ .filter(s => !q || sessionSearchText(s).includes(q));
942
+ if (!acceptedTopicKeys) {
943
+ return base.sort((a, b) => compareRows(a, b, sortKey, sortDir));
944
+ }
945
+ return base
946
+ .filter(row => acceptedTopicKeys.has(sessionTopicKey(row)))
896
947
  .sort((a, b) => compareRows(a, b, sortKey, sortDir));
897
948
  }, [filters, revision, sortDir, sortKey, storeRows, viewMode]);
898
949
  const runtimeCounts = useMemo(() => sessionRuntimeCounts(storeRows), [storeRows]);
899
950
  const rowsById = useMemo(() => new Map(storeRows.map(row => [row.sessionId, row])), [storeRows, revision]);
900
951
  const boardRows = useMemo(() => rows.filter(row => row.status !== 'closed'), [rows]);
901
- const visibleRows = viewMode === 'table' ? rows : boardRows;
952
+ const visibleRows = viewMode === 'table' || viewMode === 'topics' ? rows : boardRows;
902
953
  const selectableRows = visibleRows.filter(row => row.status !== 'closed');
903
954
  const selectedRows = [...selected]
904
955
  .map(id => rowsById.get(id))
@@ -1611,6 +1662,7 @@ function SessionsPage() {
1611
1662
  return (_jsxs("section", { className: "page sessions-page", children: [_jsxs("div", { className: "page-heading", children: [_jsxs("div", { className: "sessions-heading-main", children: [_jsx("p", { className: "eyebrow", children: t('nav.sessions') }), _jsx("h1", { children: t('sessions.title') }), _jsxs("div", { className: "sessions-view-controls", children: [_jsx("div", { className: "segmented sessions-view-toggle", role: "group", "aria-label": t('sessions.viewMode'), children: [
1612
1663
  ['kanban', t('sessions.viewKanban')],
1613
1664
  ['board', t('sessions.viewBoard')],
1665
+ ['topics', t('sessions.viewTopics')],
1614
1666
  ['table', t('sessions.viewTable')],
1615
1667
  ].map(([value, label]) => (_jsx("button", { type: "button", "data-view": value, className: viewMode === value ? 'active' : undefined, "aria-pressed": viewMode === value, onClick: () => setView(value), children: label }, value))) }), _jsx("div", { className: `segmented kanban-groupby${viewMode === 'kanban' ? ' is-visible' : ' is-collapsed'}`, id: "kanban-groupby", role: "group", "aria-label": t('sessions.kanban.groupBy'), "aria-hidden": viewMode !== 'kanban', children: [
1616
1668
  ['flow', t('sessions.kanban.groupFlow')],
@@ -1663,6 +1715,13 @@ function SessionsPage() {
1663
1715
  else
1664
1716
  next.add(row.sessionId);
1665
1717
  return next;
1718
+ }), onOpen: row => setDrawerSessionId(row.sessionId), onHistory: openHistoryModal, onLocate: row => locateSession(row), onRestart: (row, button) => void restartSession(row, button), onLock: (row, locked, button) => void setSessionLocked(row, locked, button), onClose: (row, button) => void closeSession(row, button) }), _jsx(TopicGroupsView, { rows: rows, relationRows: storeRows, selected: selected, hidden: viewMode !== 'topics', onToggleSelect: row => setSelected(prev => {
1719
+ const next = new Set(prev);
1720
+ if (next.has(row.sessionId))
1721
+ next.delete(row.sessionId);
1722
+ else
1723
+ next.add(row.sessionId);
1724
+ return next;
1666
1725
  }), onOpen: row => setDrawerSessionId(row.sessionId), onHistory: openHistoryModal, onLocate: row => locateSession(row), onRestart: (row, button) => void restartSession(row, button), onLock: (row, locked, button) => void setSessionLocked(row, locked, button), onClose: (row, button) => void closeSession(row, button) }), _jsx("div", { id: "sessions-kanban", ref: setKanbanHost, className: `sessions-kanban${kanbanGroupBy === 'bot' ? ' kanban-mode-bot' : ''}`, hidden: viewMode !== 'kanban', children: viewMode === 'kanban' ? (_jsx(SessionsKanbanView, { host: kanbanHost, ...kanbanState, canRestartSession: canRestartSession, getTeamChatIds: teamChatIdsFor, icons: {
1667
1726
  details: ICON.details,
1668
1727
  feishu: ICON.feishu,