@yancyyu/openhermit 1.6.41 → 1.6.43

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 (281) hide show
  1. package/README.md +98 -89
  2. package/bin/hermit.mjs +96 -0
  3. package/dist-renderer/assets/{ProjectEditorOverlay-Br0X83Jf.js → ProjectEditorOverlay-C98qSs7-.js} +1 -1
  4. package/dist-renderer/assets/{TeamGraphOverlay-DHMTbZPZ.js → TeamGraphOverlay-CsBbZwcL.js} +1 -1
  5. package/dist-renderer/assets/{_basePickBy-DzIiX7yH.js → _basePickBy-ZOyLWjMK.js} +1 -1
  6. package/dist-renderer/assets/{_baseUniq-6hZuzTLU.js → _baseUniq-DBb726rt.js} +1 -1
  7. package/dist-renderer/assets/{arc-CXgO6fx_.js → arc-CdiTaR_R.js} +1 -1
  8. package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-DKWgtDHr.js → architectureDiagram-VXUJARFQ-Cz3sc5TH.js} +1 -1
  9. package/dist-renderer/assets/{blockDiagram-VD42YOAC-DOMUcC40.js → blockDiagram-VD42YOAC-DE4c-KJ3.js} +1 -1
  10. package/dist-renderer/assets/{c4Diagram-YG6GDRKO-B_k2L7qX.js → c4Diagram-YG6GDRKO-CmTMDTrV.js} +1 -1
  11. package/dist-renderer/assets/channel-KTpqi9eT.js +1 -0
  12. package/dist-renderer/assets/{chunk-4BX2VUAB-BeD_ccFy.js → chunk-4BX2VUAB-rhHy3tFl.js} +1 -1
  13. package/dist-renderer/assets/{chunk-55IACEB6-ClZfkA5w.js → chunk-55IACEB6-fLZBzuo_.js} +1 -1
  14. package/dist-renderer/assets/{chunk-B4BG7PRW-5XluxXsn.js → chunk-B4BG7PRW-DOzxQhim.js} +1 -1
  15. package/dist-renderer/assets/{chunk-DI55MBZ5-BzIjjNVm.js → chunk-DI55MBZ5-COQCcXC5.js} +1 -1
  16. package/dist-renderer/assets/{chunk-FMBD7UC4-HgH3MK_H.js → chunk-FMBD7UC4-IKU9U_Y4.js} +1 -1
  17. package/dist-renderer/assets/{chunk-QN33PNHL-WeC5T3Ba.js → chunk-QN33PNHL-D6WV154X.js} +1 -1
  18. package/dist-renderer/assets/{chunk-QZHKN3VN-Cu1ApHfW.js → chunk-QZHKN3VN-D90_2DQp.js} +1 -1
  19. package/dist-renderer/assets/{chunk-TZMSLE5B-BOhlynJM.js → chunk-TZMSLE5B-BQEil57G.js} +1 -1
  20. package/dist-renderer/assets/classDiagram-2ON5EDUG-lpzulY5X.js +1 -0
  21. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-lpzulY5X.js +1 -0
  22. package/dist-renderer/assets/clone-CriGymY9.js +1 -0
  23. package/dist-renderer/assets/{cose-bilkent-S5V4N54A-DGZSihDQ.js → cose-bilkent-S5V4N54A-6WiK6U2P.js} +1 -1
  24. package/dist-renderer/assets/{dagre-6UL2VRFP-CnxwCbku.js → dagre-6UL2VRFP-DF4MMuTn.js} +1 -1
  25. package/dist-renderer/assets/{diagram-PSM6KHXK-DsIhoxdI.js → diagram-PSM6KHXK-CcF1eZ7E.js} +1 -1
  26. package/dist-renderer/assets/{diagram-QEK2KX5R-Cmh9KUF5.js → diagram-QEK2KX5R-DYlOVPQB.js} +1 -1
  27. package/dist-renderer/assets/{diagram-S2PKOQOG-CKxV456A.js → diagram-S2PKOQOG-BHXWsZOP.js} +1 -1
  28. package/dist-renderer/assets/{erDiagram-Q2GNP2WA-EnvYjOjc.js → erDiagram-Q2GNP2WA-GjmuBx8d.js} +1 -1
  29. package/dist-renderer/assets/{flowDiagram-NV44I4VS-BmNeWY_A.js → flowDiagram-NV44I4VS-BuS7YVHk.js} +1 -1
  30. package/dist-renderer/assets/{ganttDiagram-JELNMOA3-D30fyK-u.js → ganttDiagram-JELNMOA3-3Teu5tAa.js} +1 -1
  31. package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-CrUNiYg1.js → gitGraphDiagram-V2S2FVAM-BiLdCYu5.js} +1 -1
  32. package/dist-renderer/assets/{graph-CY1gTfTb.js → graph-CDP_R8ct.js} +1 -1
  33. package/dist-renderer/assets/{index-CaEbzwAU.js → index-BSZdT-g-.js} +1 -1
  34. package/dist-renderer/assets/{index-D5K-SjBG.js → index-BhWvMqsz.js} +1 -1
  35. package/dist-renderer/assets/{index-9_hO4N1e.js → index-C2_AupSj.js} +1 -1
  36. package/dist-renderer/assets/{index-59r209c1.js → index-C5ujiwAR.js} +580 -588
  37. package/dist-renderer/assets/index-CIS2CTK9.css +1 -0
  38. package/dist-renderer/assets/{index-DMR9B1UP.js → index-CVNjLwkq.js} +1 -1
  39. package/dist-renderer/assets/{index-BC2hXmg_.js → index-CwG3se0q.js} +1 -1
  40. package/dist-renderer/assets/{infoDiagram-HS3SLOUP-By_XUlcD.js → infoDiagram-HS3SLOUP-DLHUFo72.js} +1 -1
  41. package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-BM1LJE9m.js → journeyDiagram-XKPGCS4Q-BE07RpJD.js} +1 -1
  42. package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-DHIW3aTA.js → kanban-definition-3W4ZIXB7-DDHZy4NB.js} +1 -1
  43. package/dist-renderer/assets/{layout-DAKiL_Mo.js → layout-5nA5wUxO.js} +1 -1
  44. package/dist-renderer/assets/{linear-DwOaRYea.js → linear-BtF1i2qN.js} +1 -1
  45. package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-b7bJ2cha.js → mindmap-definition-VGOIOE7T-Z1Ui9Sqy.js} +1 -1
  46. package/dist-renderer/assets/{pieDiagram-ADFJNKIX-DxyL9Zr2.js → pieDiagram-ADFJNKIX-LCjxckWv.js} +1 -1
  47. package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-CR33pHlF.js → quadrantDiagram-AYHSOK5B-BOwKjSco.js} +1 -1
  48. package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-BAiSRSlh.js → requirementDiagram-UZGBJVZJ-pChP8Znd.js} +1 -1
  49. package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-C8JmDjoa.js → sankeyDiagram-TZEHDZUN-DifZ2qpo.js} +1 -1
  50. package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-c1d0Wi1m.js → sequenceDiagram-WL72ISMW-CJg-WYyY.js} +1 -1
  51. package/dist-renderer/assets/{splashScene-D0YB9uxm.js → splashScene-94xWCzLA.js} +1 -1
  52. package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-nT8BiH2O.js → stateDiagram-FKZM4ZOC-DWHOoFdv.js} +1 -1
  53. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-CGYZOoMb.js +1 -0
  54. package/dist-renderer/assets/{timeline-definition-IT6M3QCI-DpoRepUA.js → timeline-definition-IT6M3QCI-CPgokIo8.js} +1 -1
  55. package/dist-renderer/assets/{treemap-GDKQZRPO-C41UJeIH.js → treemap-GDKQZRPO-DAVqSR9L.js} +1 -1
  56. package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-KMjGARKN.js → xychartDiagram-PRI3JC2R-CCOcGbrD.js} +1 -1
  57. package/dist-renderer/chat-community-qr.jpg +0 -0
  58. package/dist-renderer/fonts/Agave-Bold.ttf +0 -0
  59. package/dist-renderer/fonts/Agave-Regular.ttf +0 -0
  60. package/dist-renderer/icon.png +0 -0
  61. package/dist-renderer/icon.rar +0 -0
  62. package/dist-renderer/index.html +3 -3
  63. package/package.json +21 -26
  64. package/src/features/worker-society/core/application/WorkerSocietyService.test.ts +802 -0
  65. package/src/features/worker-society/core/application/WorkerSocietyService.ts +428 -0
  66. package/src/features/worker-society/core/application/fakes.ts +101 -0
  67. package/src/features/worker-society/core/application/ports.ts +70 -0
  68. package/src/features/worker-society/core/domain/models/society.ts +141 -0
  69. package/src/features/worker-society/core/domain/policies/societyPolicies.test.ts +739 -0
  70. package/src/features/worker-society/core/domain/policies/societyPolicies.ts +496 -0
  71. package/src/features/worker-society/main/adapters/input/societyMcp.test.ts +317 -0
  72. package/src/features/worker-society/main/adapters/input/societyMcp.ts +257 -0
  73. package/src/features/worker-society/main/adapters/input/societyRoutes.test.ts +695 -0
  74. package/src/features/worker-society/main/adapters/input/societyRoutes.ts +194 -0
  75. package/src/features/worker-society/main/composition/societyComposition.test.ts +74 -0
  76. package/src/features/worker-society/main/composition/societyComposition.ts +70 -0
  77. package/src/features/worker-society/main/composition/workerSocietyPlugin.test.ts +69 -0
  78. package/src/features/worker-society/main/composition/workerSocietyPlugin.ts +67 -0
  79. package/src/features/worker-society/main/infrastructure/crossTeamMessageGateway.test.ts +132 -0
  80. package/src/features/worker-society/main/infrastructure/crossTeamMessageGateway.ts +84 -0
  81. package/src/features/worker-society/main/infrastructure/fsStores.test.ts +216 -0
  82. package/src/features/worker-society/main/infrastructure/fsStores.ts +113 -0
  83. package/src/features/worker-society/main/infrastructure/mergingProfileStore.test.ts +195 -0
  84. package/src/features/worker-society/main/infrastructure/mergingProfileStore.ts +96 -0
  85. package/src/features/worker-society/renderer/SocietyGraph.tsx +166 -0
  86. package/src/features/worker-society/renderer/SocietyNodeLabels.tsx +139 -0
  87. package/src/features/worker-society/renderer/SocietyNodeOverlay.tsx +339 -0
  88. package/src/features/worker-society/renderer/SocietyView.tsx +437 -0
  89. package/src/features/worker-society/renderer/index.ts +11 -0
  90. package/src/features/worker-society/renderer/societyApi.test.ts +259 -0
  91. package/src/features/worker-society/renderer/societyApi.ts +144 -0
  92. package/src/features/worker-society/renderer/societyGraphAdapter.test.ts +321 -0
  93. package/src/features/worker-society/renderer/societyGraphAdapter.ts +240 -0
  94. package/src/features/worker-society/renderer/societyOverlayActions.test.ts +57 -0
  95. package/src/features/worker-society/renderer/societyOverlayActions.ts +49 -0
  96. package/src/features/worker-society/renderer/societyStore.test.ts +218 -0
  97. package/src/features/worker-society/renderer/societyStore.ts +146 -0
  98. package/src/features/worker-society/renderer/societyViewUtils.test.ts +81 -0
  99. package/src/features/worker-society/renderer/societyViewUtils.ts +68 -0
  100. package/src/main/ipc/extensions.ts +27 -0
  101. package/src/main/server.ts +1731 -539
  102. package/src/main/services/ccConnect/CcConnectBridge.ts +26 -11
  103. package/src/main/services/ccConnect/CcConnectClient.ts +9 -2
  104. package/src/main/services/ccConnect/workDirReconcile.test.ts +57 -0
  105. package/src/main/services/ccConnect/workDirReconcile.ts +36 -0
  106. package/src/main/services/direct-cli/DirectCliSessionManager.test.ts +397 -0
  107. package/src/main/services/direct-cli/DirectCliSessionManager.ts +508 -0
  108. package/src/main/services/direct-cli/DirectCliSessionStore.test.ts +79 -0
  109. package/src/main/services/direct-cli/DirectCliSessionStore.ts +97 -0
  110. package/src/main/services/direct-cli/__tests__/directCliMessageId.test.ts +40 -0
  111. package/src/main/services/direct-cli/directCliMessageId.ts +21 -0
  112. package/src/main/services/direct-cli/index.ts +17 -0
  113. package/src/main/services/extensions/capability-packs/CapabilityPackLoaderService.ts +637 -0
  114. package/src/main/services/extensions/catalog/PluginCatalogService.ts +2 -2
  115. package/src/main/services/loop-assets/LoopAssetsScannerService.ts +657 -0
  116. package/src/main/services/runtime/providerAwareCliEnv.ts +33 -5
  117. package/src/main/services/session-intelligence/LocalSessionScanner.ts +156 -71
  118. package/src/main/services/session-intelligence/SessionUsageParser.ts +103 -8
  119. package/src/main/services/session-intelligence/UsageTelemetryService.ts +11 -0
  120. package/src/main/services/session-intelligence/__tests__/teamSessionListMapper.test.ts +104 -0
  121. package/src/main/services/session-intelligence/teamSessionListMapper.ts +78 -0
  122. package/src/main/services/system-manager/AdminLoopInitializer.ts +95 -0
  123. package/src/main/services/system-manager/BuiltinWorkflowSeeder.ts +744 -0
  124. package/src/main/services/system-manager/SystemManagerConfigService.ts +19 -1
  125. package/src/main/services/system-manager/WorkflowPromptService.ts +58 -5
  126. package/src/main/services/system-manager/__tests__/AdminLoopInitializer.test.ts +129 -0
  127. package/src/main/services/system-manager/__tests__/SystemManagerConfigService.test.ts +60 -0
  128. package/src/main/services/teams-mvp/CollaborationBoardService.ts +2 -0
  129. package/src/main/services/teams-mvp/OpsRunbookContext.ts +60 -0
  130. package/src/main/services/teams-mvp/TaskDispatchService.test.ts +305 -0
  131. package/src/main/services/teams-mvp/TaskDispatchService.ts +250 -131
  132. package/src/main/services/teams-mvp/TeamProvisioningService.ts +12 -2
  133. package/src/main/services/teams-mvp/TeamWorkspaceService.test.ts +207 -0
  134. package/src/main/services/teams-mvp/TeamWorkspaceService.ts +104 -51
  135. package/src/main/services/teams-mvp/index.ts +6 -0
  136. package/src/main/utils/externalPlatformSessionRouting.ts +92 -0
  137. package/src/main/utils/toolApprovalRules.ts +151 -0
  138. package/src/renderer/App.tsx +24 -89
  139. package/src/renderer/api/httpClient.ts +132 -52
  140. package/src/renderer/api/providers.ts +5 -16
  141. package/src/renderer/components/chat/CommunityChatView.tsx +81 -0
  142. package/src/renderer/components/dashboard/DashboardView.tsx +130 -84
  143. package/src/renderer/components/extensions/ExtensionStoreView.tsx +39 -5
  144. package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +2 -1
  145. package/src/renderer/components/extensions/capability-packs/CapabilityPacksPanel.tsx +170 -0
  146. package/src/renderer/components/layout/PaneContent.tsx +10 -2
  147. package/src/renderer/components/layout/SortableTab.tsx +4 -0
  148. package/src/renderer/components/layout/TabBarActions.tsx +13 -16
  149. package/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx +4 -135
  150. package/src/renderer/components/schedules/SchedulesView.tsx +22 -14
  151. package/src/renderer/components/schedules/calendar/CalendarEventBlock.tsx +7 -6
  152. package/src/renderer/components/settings/SettingsTabs.tsx +24 -21
  153. package/src/renderer/components/settings/SettingsView.tsx +22 -13
  154. package/src/renderer/components/settings/components/SettingRow.tsx +13 -5
  155. package/src/renderer/components/settings/components/SettingsSectionCard.tsx +53 -0
  156. package/src/renderer/components/settings/components/SettingsSectionHeader.tsx +10 -6
  157. package/src/renderer/components/settings/components/SettingsSelect.tsx +12 -9
  158. package/src/renderer/components/settings/components/SettingsToggle.tsx +6 -5
  159. package/src/renderer/components/settings/components/index.ts +1 -0
  160. package/src/renderer/components/settings/sections/AdvancedSection.tsx +78 -59
  161. package/src/renderer/components/settings/sections/CliStatusSection.tsx +32 -44
  162. package/src/renderer/components/settings/sections/ConfigEditorDialog.tsx +1 -1
  163. package/src/renderer/components/settings/sections/GeneralSection.tsx +216 -186
  164. package/src/renderer/components/settings/sections/PlatformsSection.tsx +25 -17
  165. package/src/renderer/components/settings/sections/TaskBusSection.tsx +63 -22
  166. package/src/renderer/components/sidebar/SidebarSessions.tsx +120 -80
  167. package/src/renderer/components/sidebar/SidebarTaskItem.tsx +1 -1
  168. package/src/renderer/components/splash/splashScene.ts +6 -2
  169. package/src/renderer/components/system-manager/SystemManagerView.tsx +169 -255
  170. package/src/renderer/components/tasks/TasksView.tsx +63 -37
  171. package/src/renderer/components/team/CcSessionsSection.tsx +124 -89
  172. package/src/renderer/components/team/HarnessBrandLogos.tsx +318 -0
  173. package/src/renderer/components/team/HarnessSelect.tsx +25 -26
  174. package/src/renderer/components/team/TeamDetailView.tsx +137 -153
  175. package/src/renderer/components/team/TeamEmptyState.tsx +9 -37
  176. package/src/renderer/components/team/TeamListView.tsx +144 -30
  177. package/src/renderer/components/team/__tests__/CcSessionsSection.hasLocalFile.test.tsx +128 -0
  178. package/src/renderer/components/team/activity/ActivityItem.tsx +21 -9
  179. package/src/renderer/components/team/activity/ActivityTimeline.tsx +2 -2
  180. package/src/renderer/components/team/dialogs/AdvancedCliSection.tsx +1 -1
  181. package/src/renderer/components/team/dialogs/CreateTaskDialog.tsx +13 -10
  182. package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +189 -57
  183. package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +9 -157
  184. package/src/renderer/components/team/dialogs/LaunchTeamDialog.tsx +19 -15
  185. package/src/renderer/components/team/dialogs/PlatformBindingDialog.tsx +48 -10
  186. package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +11 -12
  187. package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +39 -37
  188. package/src/renderer/components/team/dialogs/RuntimeConfigDialog.tsx +434 -64
  189. package/src/renderer/components/team/dialogs/SendMessageDialog.tsx +12 -10
  190. package/src/renderer/components/team/dialogs/TaskDetailDialog.tsx +2 -2
  191. package/src/renderer/components/team/dialogs/__tests__/CreateTeamDialog.bindProject.test.tsx +399 -0
  192. package/src/renderer/components/team/dialogs/__tests__/CreateTeamDialog.chineseRepro.test.tsx +253 -0
  193. package/src/renderer/components/team/dialogs/platformAllowUtils.ts +91 -0
  194. package/src/renderer/components/team/dialogs/teammateRuntimeCompatibility.tsx +1 -1
  195. package/src/renderer/components/team/kanban/KanbanTaskCard.test.tsx +41 -0
  196. package/src/renderer/components/team/kanban/KanbanTaskCard.tsx +41 -86
  197. package/src/renderer/components/team/loop-console/LoopCommandComposer.tsx +310 -0
  198. package/src/renderer/components/team/loop-console/LoopConsolePanel.tsx +372 -0
  199. package/src/renderer/components/team/loop-console/loopSendIntent.test.ts +85 -0
  200. package/src/renderer/components/team/loop-console/loopSendIntent.ts +221 -0
  201. package/src/renderer/components/team/loop-console/useLeadSessionToolActivity.ts +74 -0
  202. package/src/renderer/components/team/loop-console/useLoopCommandSuggestions.ts +165 -0
  203. package/src/renderer/components/team/loop-console/useLoopConsoleController.ts +266 -0
  204. package/src/renderer/components/team/members/LeadModelRow.test.tsx +1 -1
  205. package/src/renderer/components/team/members/LeadModelRow.tsx +5 -3
  206. package/src/renderer/components/team/members/MemberDetailDialog.tsx +11 -0
  207. package/src/renderer/components/team/members/MemberDetailStats.tsx +13 -3
  208. package/src/renderer/components/team/members/MemberDraftRow.test.tsx +1 -1
  209. package/src/renderer/components/team/members/MemberDraftRow.tsx +1 -1
  210. package/src/renderer/components/team/members/MemberMessagesTab.tsx +2 -2
  211. package/src/renderer/components/team/members/MemberStatsTab.tsx +1 -1
  212. package/src/renderer/components/team/messages/MessageComposer.tsx +150 -44
  213. package/src/renderer/components/team/messages/MessagesFilterPopover.tsx +2 -2
  214. package/src/renderer/components/team/messages/MessagesPanel.tsx +34 -28
  215. package/src/renderer/components/team/schedule/CcCronScheduleDialog.tsx +6 -6
  216. package/src/renderer/components/team/taskLogs/ExactTaskLogCard.tsx +2 -2
  217. package/src/renderer/components/team/taskLogs/TaskLogStreamSection.tsx +1 -1
  218. package/src/renderer/components/terminal/TerminalPanel.tsx +2 -3
  219. package/src/renderer/components/ui/MentionableTextarea.tsx +5 -1
  220. package/src/renderer/constants/teamColors.ts +5 -5
  221. package/src/renderer/hooks/useExtensionsTabState.ts +1 -1
  222. package/src/renderer/hooks/useMentionDetection.ts +5 -1
  223. package/src/renderer/hooks/useProjectWorkflowCommands.ts +57 -0
  224. package/src/renderer/hooks/useTeamSuggestions.ts +2 -0
  225. package/src/renderer/index.css +19 -2
  226. package/src/renderer/main.tsx +7 -1
  227. package/src/renderer/store/index.ts +18 -1
  228. package/src/renderer/store/slices/extensionsSlice.ts +83 -0
  229. package/src/renderer/store/slices/tabSlice.ts +61 -0
  230. package/src/renderer/store/slices/teamSlice.ts +138 -9
  231. package/src/renderer/types/mention.ts +8 -0
  232. package/src/renderer/types/tabs.ts +3 -1
  233. package/src/renderer/utils/__tests__/bindProjectSlug.test.ts +69 -0
  234. package/src/renderer/utils/__tests__/groupTransformer.test.ts +148 -0
  235. package/src/renderer/utils/__tests__/initialRoute.test.ts +101 -0
  236. package/src/renderer/utils/__tests__/leadToolActivity.test.ts +124 -0
  237. package/src/renderer/utils/__tests__/mergeTeamMessages.test.ts +81 -0
  238. package/src/renderer/utils/__tests__/teamMessageFiltering.test.ts +213 -0
  239. package/src/renderer/utils/__tests__/teamMessageKey.test.ts +75 -0
  240. package/src/renderer/utils/__tests__/workflowCommandExecution.test.ts +173 -0
  241. package/src/renderer/utils/__tests__/workflowCommandSuggestions.test.ts +59 -0
  242. package/src/renderer/utils/bindProjectSlug.ts +57 -0
  243. package/src/renderer/utils/capabilityCommandExecution.ts +113 -0
  244. package/src/renderer/utils/initialRoute.ts +89 -0
  245. package/src/renderer/utils/leadToolActivity.ts +117 -0
  246. package/src/renderer/utils/loopShortcutSuggestions.ts +106 -0
  247. package/src/renderer/utils/mentionSuggestions.ts +1 -1
  248. package/src/renderer/utils/slashCommandRegistry.ts +231 -0
  249. package/src/renderer/utils/teamMentionDirective.ts +31 -0
  250. package/src/renderer/utils/workflowCommandExecution.ts +96 -0
  251. package/src/renderer/utils/workflowCommandSuggestions.ts +49 -0
  252. package/src/shared/types/api.ts +79 -4
  253. package/src/shared/types/ccConnect.ts +1 -0
  254. package/src/shared/types/extensions/api.ts +19 -0
  255. package/src/shared/types/extensions/capabilityPack.ts +118 -0
  256. package/src/shared/types/extensions/index.ts +29 -1
  257. package/src/shared/types/index.ts +6 -0
  258. package/src/shared/types/loopAssets.ts +54 -0
  259. package/src/shared/types/providers.ts +0 -16
  260. package/src/shared/types/systemManager.ts +26 -1
  261. package/src/shared/types/team.ts +43 -3
  262. package/src/shared/types/terminal.ts +2 -36
  263. package/src/shared/types/worker.test.ts +28 -0
  264. package/src/shared/types/worker.ts +3 -0
  265. package/src/shared/utils/__tests__/effortLevels.test.ts +88 -0
  266. package/src/shared/utils/__tests__/providerBackend.test.ts +88 -0
  267. package/src/shared/utils/__tests__/providerLaunchArgs.test.ts +220 -0
  268. package/src/shared/utils/claudeStreamJson.test.ts +187 -0
  269. package/src/shared/utils/claudeStreamJson.ts +153 -0
  270. package/src/shared/utils/providerLaunchArgs.ts +217 -0
  271. package/src/shared/utils/slashCommands.ts +10 -0
  272. package/src/types/node-pty.d.ts +8 -0
  273. package/dist-renderer/assets/channel-D0XS_akr.js +0 -1
  274. package/dist-renderer/assets/classDiagram-2ON5EDUG-D13Ffs0U.js +0 -1
  275. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-D13Ffs0U.js +0 -1
  276. package/dist-renderer/assets/clone-B1ZrxI1D.js +0 -1
  277. package/dist-renderer/assets/index-iyjkpSus.css +0 -32
  278. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-Dmibmlso.js +0 -1
  279. package/src/main/services/system-manager/SystemManagerPtyService.ts +0 -233
  280. package/src/renderer/components/common/TerminalPane.tsx +0 -213
  281. package/src/renderer/components/team/dialogs/useTeamEditForm.ts +0 -292
@@ -78,7 +78,7 @@ export const CcCronScheduleDialog = ({
78
78
  const normalizedTimeout = Number.parseInt(timeoutMins, 10);
79
79
 
80
80
  if (!normalizedTeamName) {
81
- setError('请选择团队');
81
+ setError('请选择 Loop workspace');
82
82
  return;
83
83
  }
84
84
  if (!normalizedCron) {
@@ -86,7 +86,7 @@ export const CcCronScheduleDialog = ({
86
86
  return;
87
87
  }
88
88
  if (!normalizedPrompt) {
89
- setError('请填写定时发送的消息');
89
+ setError('请填写定时指令');
90
90
  return;
91
91
  }
92
92
 
@@ -138,13 +138,13 @@ export const CcCronScheduleDialog = ({
138
138
  <div className="space-y-4">
139
139
  {canChooseTeam ? (
140
140
  <div className="space-y-1.5">
141
- <Label>团队</Label>
141
+ <Label>Loop workspace</Label>
142
142
  <select
143
143
  value={selectedTeamName}
144
144
  onChange={(event) => setSelectedTeamName(event.target.value)}
145
145
  className="h-9 w-full rounded-md border border-[var(--color-border)] bg-[var(--color-surface)] px-2 text-sm text-[var(--color-text)]"
146
146
  >
147
- <option value="">选择团队...</option>
147
+ <option value="">选择 Loop workspace...</option>
148
148
  {teams.map((team) => (
149
149
  <option key={team.teamName} value={team.teamName}>
150
150
  {team.displayName || team.teamName}
@@ -175,11 +175,11 @@ export const CcCronScheduleDialog = ({
175
175
  </div>
176
176
 
177
177
  <div className="space-y-1.5">
178
- <Label>定时消息</Label>
178
+ <Label>定时指令</Label>
179
179
  <Textarea
180
180
  value={prompt}
181
181
  onChange={(event) => setPrompt(event.target.value)}
182
- placeholder="到时间后发送给团队负责人的消息..."
182
+ placeholder="到时间后发送给 Lead 的循环指令..."
183
183
  className="min-h-28"
184
184
  />
185
185
  </div>
@@ -30,7 +30,7 @@ function actorLabel(summary: BoardTaskExactLogSummary): string {
30
30
  return summary.actor.memberName;
31
31
  }
32
32
  if (summary.actor.role === 'lead' || summary.actor.isSidechain === false) {
33
- return '负责人会话';
33
+ return 'Loop Lead 会话';
34
34
  }
35
35
  return '未知参与者';
36
36
  }
@@ -40,7 +40,7 @@ function describeSummary(summary: BoardTaskExactLogSummary): string {
40
40
  }
41
41
 
42
42
  function anchorKindLabel(summary: BoardTaskExactLogSummary): string {
43
- return summary.anchorKind === 'tool' ? '工具' : '消息';
43
+ return summary.anchorKind === 'tool' ? '工具' : '动态';
44
44
  }
45
45
 
46
46
  function describeDetailState(state: ExactTaskLogDetailState | undefined): string | null {
@@ -39,7 +39,7 @@ function actorLabel(actor: BoardTaskLogActor): string {
39
39
  return actor.memberName;
40
40
  }
41
41
  if (actor.role === 'lead' || actor.isSidechain === false) {
42
- return '负责人会话';
42
+ return 'Loop Lead 会话';
43
43
  }
44
44
  if (actor.agentId) {
45
45
  return `成员 ${actor.agentId.slice(0, 8)}`;
@@ -9,9 +9,8 @@
9
9
  * final frame instead of dumping every intermediate line
10
10
  * - optional `$ command` prompt line so a Bash call reads like a real terminal
11
11
  *
12
- * It is intentionally lightweight (no xterm.js / node-pty): the session view is a
13
- * read-only viewer of recorded output, so we only need faithful rendering, not a
14
- * live PTY.
12
+ * It is intentionally lightweight: the session view is a read-only viewer of
13
+ * recorded output, so we only need faithful rendering, not a live PTY.
15
14
  */
16
15
 
17
16
  import { useMemo, useState } from 'react';
@@ -356,6 +356,8 @@ interface MentionableTextareaProps extends Omit<
356
356
  taskSuggestions?: MentionSuggestion[];
357
357
  /** Slash command suggestions for /command autocomplete */
358
358
  commandSuggestions?: MentionSuggestion[];
359
+ /** Called after a suggestion is inserted into the textarea. */
360
+ onSuggestionSelected?: (suggestion: MentionSuggestion, insertedText: string) => void;
359
361
  /** Called when Enter (without Shift) is pressed. */
360
362
  onModEnter?: () => void;
361
363
  /** Called when Shift+Tab is pressed. */
@@ -388,6 +390,7 @@ export const MentionableTextarea = React.forwardRef<HTMLTextAreaElement, Mention
388
390
  teamSuggestions = [],
389
391
  taskSuggestions = [],
390
392
  commandSuggestions = [],
393
+ onSuggestionSelected,
391
394
  onModEnter,
392
395
  onShiftTab,
393
396
  dismissMentionsRef,
@@ -454,6 +457,7 @@ export const MentionableTextarea = React.forwardRef<HTMLTextAreaElement, Mention
454
457
  if (trigger.triggerChar !== '/') return true;
455
458
  return text.slice(0, trigger.triggerIndex).trim().length === 0;
456
459
  },
460
+ onSuggestionSelected,
457
461
  });
458
462
 
459
463
  // Expose dismiss to parent via ref for external close (e.g. Send button click)
@@ -1069,7 +1073,7 @@ export const MentionableTextarea = React.forwardRef<HTMLTextAreaElement, Mention
1069
1073
  const rotatingTips = React.useMemo(
1070
1074
  () => [
1071
1075
  'Tips:输入 @ 可提及成员、团队或文件,输入 # 可引用任务。',
1072
- 'Tips:不要把所有工作都堆给团队负责人,可以让负责人分配给合适的成员。',
1076
+ 'Tips:不要把所有工作都堆给 Loop Lead,可以让 Lead 把循环分配给合适的成员。',
1073
1077
  ...extraTips,
1074
1078
  ],
1075
1079
  [extraTips]
@@ -24,11 +24,11 @@ export interface TeamColorSet {
24
24
 
25
25
  const TEAMMATE_COLORS: Record<string, TeamColorSet> = {
26
26
  blue: {
27
- border: '#6366f1',
28
- badge: 'rgba(99, 102, 241, 0.15)',
29
- badgeLight: 'rgba(99, 102, 241, 0.12)',
30
- text: '#818cf8',
31
- textLight: '#4f46e5',
27
+ border: '#3b82f6',
28
+ badge: 'rgba(59, 130, 246, 0.15)',
29
+ badgeLight: 'rgba(59, 130, 246, 0.12)',
30
+ text: '#60a5fa',
31
+ textLight: '#2563eb',
32
32
  },
33
33
  saffron: {
34
34
  border: '#eab308',
@@ -8,7 +8,7 @@ import { useCallback, useMemo, useState } from 'react';
8
8
 
9
9
  import type { PluginCapability, PluginFilters, PluginSortField } from '@shared/types/extensions';
10
10
 
11
- export type ExtensionsSubTab = 'plugins';
11
+ export type ExtensionsSubTab = 'plugins' | 'capability-packs';
12
12
 
13
13
  interface PluginSortState {
14
14
  field: PluginSortField;
@@ -17,6 +17,8 @@ interface UseMentionDetectionOptions {
17
17
  isTriggerEnabled?: (triggerChar: string) => boolean;
18
18
  /** Additional validation for trigger matches before opening the dropdown. */
19
19
  isTriggerMatchValid?: (trigger: MentionTrigger, text: string) => boolean;
20
+ /** Called after a suggestion is inserted into the textarea. */
21
+ onSuggestionSelected?: (suggestion: MentionSuggestion, insertedText: string) => void;
20
22
  }
21
23
 
22
24
  export interface DropdownPosition {
@@ -179,6 +181,7 @@ export function useMentionDetection({
179
181
  triggerChars = ['@'],
180
182
  isTriggerEnabled,
181
183
  isTriggerMatchValid,
184
+ onSuggestionSelected,
182
185
  }: UseMentionDetectionOptions): UseMentionDetectionResult {
183
186
  const [isOpen, setIsOpen] = useState(false);
184
187
  const [activeTriggerChar, setActiveTriggerChar] = useState<string | null>(null);
@@ -234,6 +237,7 @@ export function useMentionDetection({
234
237
  const newCursorPos = before.length + insertion.length;
235
238
 
236
239
  onValueChange(newValue);
240
+ onSuggestionSelected?.(s, insertionBody);
237
241
  dismiss();
238
242
 
239
243
  // Set cursor position after React re-render
@@ -242,7 +246,7 @@ export function useMentionDetection({
242
246
  textarea.setSelectionRange(newCursorPos, newCursorPos);
243
247
  });
244
248
  },
245
- [value, onValueChange, textareaRef, dismiss]
249
+ [value, onValueChange, onSuggestionSelected, textareaRef, dismiss]
246
250
  );
247
251
 
248
252
  /**
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Load a team project's `.claude/commands/*.md` workflow prompts and expose them
3
+ * as command suggestions for the Loop console.
4
+ *
5
+ * The team Loop console previously only showed global shortcuts + capability
6
+ * packs, so a project's own commands never appeared. This mirrors the admin
7
+ * console's workflow loading, scoped to the team's project path.
8
+ */
9
+ import { useEffect, useState } from 'react';
10
+
11
+ import { api } from '@renderer/api';
12
+ import { buildWorkflowCommandSuggestion } from '@renderer/utils/workflowCommandSuggestions';
13
+
14
+ import type { MentionSuggestion } from '@renderer/types/mention';
15
+
16
+ const EMPTY: MentionSuggestion[] = [];
17
+
18
+ function commandsFolder(projectPath: string): string {
19
+ return `${projectPath.replace(/[\\/]+$/, '')}/.claude/commands`;
20
+ }
21
+
22
+ export function useProjectWorkflowCommands(projectPath?: string | null): MentionSuggestion[] {
23
+ const [suggestions, setSuggestions] = useState<MentionSuggestion[]>(EMPTY);
24
+
25
+ useEffect(() => {
26
+ const trimmed = projectPath?.trim();
27
+ if (!trimmed) {
28
+ setSuggestions(EMPTY);
29
+ return;
30
+ }
31
+ let cancelled = false;
32
+ void (async () => {
33
+ try {
34
+ const result = await api.systemManager.listWorkflowPrompts(commandsFolder(trimmed));
35
+ if (cancelled) return;
36
+ const seen = new Set<string>();
37
+ const next = result.prompts
38
+ .map((prompt) => buildWorkflowCommandSuggestion(prompt, 'team-workflow'))
39
+ .filter((suggestion) => {
40
+ const key = suggestion.command ?? suggestion.id;
41
+ if (seen.has(key)) return false;
42
+ seen.add(key);
43
+ return true;
44
+ });
45
+ setSuggestions(next);
46
+ } catch {
47
+ // Missing .claude/commands is normal — the project simply has no custom commands.
48
+ if (!cancelled) setSuggestions(EMPTY);
49
+ }
50
+ })();
51
+ return () => {
52
+ cancelled = true;
53
+ };
54
+ }, [projectPath]);
55
+
56
+ return suggestions;
57
+ }
@@ -61,6 +61,8 @@ export function useTeamSuggestions(currentTeamName: string | null): UseTeamSugge
61
61
  color: t.color,
62
62
  type: 'team' as const,
63
63
  isOnline,
64
+ insertText: t.teamName,
65
+ searchText: [t.teamName, t.displayName].filter(Boolean).join(' '),
64
66
  };
65
67
  });
66
68
 
@@ -24,6 +24,11 @@
24
24
  --color-text-secondary: #7a8190;
25
25
  --color-text-muted: #4f5564;
26
26
  --color-accent: #4ade80;
27
+ --color-accent-rgb: 74, 222, 128;
28
+ --color-accent-soft: rgba(74, 222, 128, 0.08);
29
+ --color-accent-muted: rgba(74, 222, 128, 0.14);
30
+ --color-accent-border: rgba(74, 222, 128, 0.32);
31
+ --color-accent-glow: rgba(74, 222, 128, 0.24);
27
32
  /* Accent — phosphor green */
28
33
  /* Scrollbar colors */
29
34
  --scrollbar-thumb: rgba(74, 222, 128, 0.12);
@@ -473,6 +478,11 @@
473
478
  --color-text-muted: #6d6b65;
474
479
  /* Warm muted text */
475
480
  --color-accent: #4f46e5;
481
+ --color-accent-rgb: 79, 70, 229;
482
+ --color-accent-soft: rgba(79, 70, 229, 0.08);
483
+ --color-accent-muted: rgba(79, 70, 229, 0.14);
484
+ --color-accent-border: rgba(79, 70, 229, 0.32);
485
+ --color-accent-glow: rgba(79, 70, 229, 0.22);
476
486
  /* Accent — indigo-600, visible on light surfaces */
477
487
  /* Assessment severity colors - darker for light backgrounds */
478
488
  --assess-good: #16a34a;
@@ -797,8 +807,15 @@ body {
797
807
  height: 100%;
798
808
  overflow: hidden;
799
809
  font-family:
800
- 'Agave', 'JetBrains Mono', 'Menlo', 'Monaco', 'Consolas',
801
- -apple-system, BlinkMacSystemFont, 'Segoe UI', monospace;
810
+ 'Agave',
811
+ 'JetBrains Mono',
812
+ 'Menlo',
813
+ 'Monaco',
814
+ 'Consolas',
815
+ -apple-system,
816
+ BlinkMacSystemFont,
817
+ 'Segoe UI',
818
+ monospace;
802
819
  -webkit-font-smoothing: antialiased;
803
820
  -moz-osx-font-smoothing: grayscale;
804
821
  background-color: var(--color-surface);
@@ -7,7 +7,8 @@ import ReactDOM from 'react-dom/client';
7
7
 
8
8
  import { App } from './App';
9
9
  import { initSentryRenderer } from './sentry';
10
- import { initializeNotificationListeners } from './store';
10
+ import { initializeNotificationListeners, useStore } from './store';
11
+ import { restoreInitialRoute } from './utils/initialRoute';
11
12
 
12
13
  declare global {
13
14
  interface Window {
@@ -23,6 +24,11 @@ if (!window.__claudeTeamsUiDidInit) {
23
24
  initializeNotificationListeners();
24
25
  }
25
26
 
27
+ // Restore the URL route into the store BEFORE first render so the initial paint
28
+ // already shows the correct tab. This closes the first-load blank-content race
29
+ // (e.g. /teams rendering empty until a post-mount effect opened the tab).
30
+ restoreInitialRoute(useStore.getState(), window.location.pathname);
31
+
26
32
  ReactDOM.createRoot(document.getElementById('root')!).render(
27
33
  <React.StrictMode>
28
34
  <App />
@@ -1216,6 +1216,23 @@ export function initializeNotificationListeners(): () => void {
1216
1216
  return;
1217
1217
  }
1218
1218
 
1219
+ // Live token stream from a direct-CLI subprocess: accumulate each text delta into
1220
+ // an in-progress optimistic assistant reply keyed by messageId. The canonical reply
1221
+ // (same messageId, appended on the turn's result) prunes this twin on the next inbox
1222
+ // refresh — so no duplicate, and a missing/failed turn just leaves a partial bubble.
1223
+ if (event.type === 'direct-cli-stream') {
1224
+ if (isStaleRuntimeEvent) return;
1225
+ if (event.kind === 'delta' && event.messageId && typeof event.text === 'string') {
1226
+ useStore.getState().appendStreamingTeamReply(event.teamName, {
1227
+ messageId: event.messageId,
1228
+ delta: event.text,
1229
+ from: event.from ?? event.teamName,
1230
+ to: 'user',
1231
+ });
1232
+ }
1233
+ return;
1234
+ }
1235
+
1219
1236
  if (event.type === 'log-source-change') {
1220
1237
  if (!event?.teamName || !isTeamVisibleInAnyPane(event.teamName)) {
1221
1238
  return;
@@ -1422,7 +1439,7 @@ export function initializeNotificationListeners(): () => void {
1422
1439
  useStore.setState({ cliInstallerState: 'verifying', cliInstallerDetail: detail });
1423
1440
  break;
1424
1441
  case 'installing': {
1425
- // Accumulate log lines and raw chunks for xterm.js rendering
1442
+ // Accumulate log lines and raw chunks for terminal-style rendering
1426
1443
  const prevLogs = useStore.getState().cliInstallerLogs;
1427
1444
  const prevRaw = useStore.getState().cliInstallerRawChunks;
1428
1445
  const newLogs = detail ? [...prevLogs, detail].slice(-50) : prevLogs;
@@ -18,10 +18,15 @@ import { findPaneByTabId, updatePane } from '../utils/paneHelpers';
18
18
 
19
19
  import type { AppState } from '../types';
20
20
  import type {
21
+ CapabilityPackExportRequest,
22
+ CapabilityPackImportRequest,
23
+ CapabilityPackListResult,
24
+ CapabilityPackMutationResult,
21
25
  EnrichedPlugin,
22
26
  ExtensionOperationState,
23
27
  InstalledMcpEntry,
24
28
  InstallScope,
29
+ LoadedCapabilityPack,
25
30
  McpCatalogItem,
26
31
  McpCustomInstallRequest,
27
32
  McpInstallRequest,
@@ -79,6 +84,14 @@ export interface ExtensionsSlice {
79
84
  message?: string;
80
85
  }>;
81
86
 
87
+ // ── Capability packs cache ──
88
+ capabilityPackList: CapabilityPackListResult | null;
89
+ capabilityPacks: LoadedCapabilityPack[];
90
+ capabilityPacksLoading: boolean;
91
+ capabilityPacksError: string | null;
92
+ capabilityPacksMutationLoading: boolean;
93
+ capabilityPacksMutationError: string | null;
94
+
82
95
  // ── Skills catalog cache ──
83
96
  skillsUserCatalog: SkillCatalogItem[];
84
97
  skillsProjectCatalogByProjectPath: Record<string, SkillCatalogItem[]>;
@@ -101,6 +114,13 @@ export interface ExtensionsSlice {
101
114
  mcpBrowse: (cursor?: string) => Promise<void>;
102
115
  mcpFetchInstalled: (projectPath?: string) => Promise<void>;
103
116
  runMcpDiagnostics: (projectPath?: string) => Promise<void>;
117
+ fetchCapabilityPacks: () => Promise<void>;
118
+ importCapabilityPack: (
119
+ request: CapabilityPackImportRequest
120
+ ) => Promise<CapabilityPackMutationResult>;
121
+ exportCapabilityPack: (
122
+ request: CapabilityPackExportRequest
123
+ ) => Promise<CapabilityPackMutationResult>;
104
124
  fetchSkillsCatalog: (projectPath?: string) => Promise<void>;
105
125
  fetchSkillDetail: (skillId: string, projectPath?: string) => Promise<void>;
106
126
  previewSkillUpsert: (request: SkillUpsertRequest) => Promise<SkillReviewPreview>;
@@ -394,6 +414,13 @@ export const createExtensionsSlice: StateCreator<AppState, [], [], ExtensionsSli
394
414
  installErrors: {},
395
415
  extensionToasts: [],
396
416
 
417
+ capabilityPackList: null,
418
+ capabilityPacks: [],
419
+ capabilityPacksLoading: false,
420
+ capabilityPacksError: null,
421
+ capabilityPacksMutationLoading: false,
422
+ capabilityPacksMutationError: null,
423
+
397
424
  skillsUserCatalog: [],
398
425
  skillsProjectCatalogByProjectPath: {},
399
426
  skillsCatalogLoadingByProjectPath: {},
@@ -668,6 +695,62 @@ export const createExtensionsSlice: StateCreator<AppState, [], [], ExtensionsSli
668
695
  await promise;
669
696
  },
670
697
 
698
+ fetchCapabilityPacks: async () => {
699
+ if (!api.capabilityPacks) return;
700
+
701
+ set({ capabilityPacksLoading: true, capabilityPacksError: null });
702
+ try {
703
+ const result = await api.capabilityPacks.list();
704
+ set({
705
+ capabilityPackList: result,
706
+ capabilityPacks: result.packs,
707
+ capabilityPacksLoading: false,
708
+ capabilityPacksError: null,
709
+ });
710
+ } catch (err) {
711
+ set({
712
+ capabilityPacksLoading: false,
713
+ capabilityPacksError:
714
+ err instanceof Error ? err.message : 'Failed to load capability packs',
715
+ });
716
+ }
717
+ },
718
+
719
+ importCapabilityPack: async (request: CapabilityPackImportRequest) => {
720
+ if (!api.capabilityPacks) {
721
+ throw new Error('Capability packs API is not available');
722
+ }
723
+
724
+ set({ capabilityPacksMutationLoading: true, capabilityPacksMutationError: null });
725
+ try {
726
+ const result = await api.capabilityPacks.importPack(request);
727
+ await get().fetchCapabilityPacks();
728
+ set({ capabilityPacksMutationLoading: false });
729
+ return result;
730
+ } catch (err) {
731
+ const message = err instanceof Error ? err.message : 'Failed to import capability pack';
732
+ set({ capabilityPacksMutationLoading: false, capabilityPacksMutationError: message });
733
+ throw err;
734
+ }
735
+ },
736
+
737
+ exportCapabilityPack: async (request: CapabilityPackExportRequest) => {
738
+ if (!api.capabilityPacks) {
739
+ throw new Error('Capability packs API is not available');
740
+ }
741
+
742
+ set({ capabilityPacksMutationLoading: true, capabilityPacksMutationError: null });
743
+ try {
744
+ const result = await api.capabilityPacks.exportPack(request);
745
+ set({ capabilityPacksMutationLoading: false });
746
+ return result;
747
+ } catch (err) {
748
+ const message = err instanceof Error ? err.message : 'Failed to export capability pack';
749
+ set({ capabilityPacksMutationLoading: false, capabilityPacksMutationError: message });
750
+ throw err;
751
+ }
752
+ },
753
+
671
754
  fetchSkillsCatalog: async (projectPath?: string) => {
672
755
  if (!api.skills) return;
673
756
 
@@ -52,6 +52,8 @@ export interface TabSlice {
52
52
  closeTab: (tabId: string) => void;
53
53
  setActiveTab: (tabId: string) => void;
54
54
  openDashboard: () => void;
55
+ openSocietyTab: () => void;
56
+ openChatTab: () => void;
55
57
  openSessionReport: (sourceTabId: string) => void;
56
58
  getActiveTab: () => Tab | null;
57
59
  isSessionOpen: (sessionId: string) => boolean;
@@ -455,6 +457,65 @@ export const createTabSlice: StateCreator<AppState, [], [], TabSlice> = (set, ge
455
457
  set(syncFromLayout(newLayout));
456
458
  },
457
459
 
460
+ // Open a worker-society tab — reuse existing one if found, otherwise create new
461
+ openSocietyTab: () => {
462
+ const state = get();
463
+ const { paneLayout } = state;
464
+
465
+ const existing = getAllTabs(paneLayout).find((t) => t.type === 'society');
466
+ if (existing) {
467
+ const pane = findPaneByTabId(paneLayout, existing.id);
468
+ if (pane) {
469
+ const fromIndex = pane.tabs.findIndex((t) => t.id === existing.id);
470
+ const lastIndex = pane.tabs.length - 1;
471
+ if (fromIndex !== -1 && fromIndex !== lastIndex) {
472
+ const reordered = [...pane.tabs];
473
+ const [moved] = reordered.splice(fromIndex, 1);
474
+ reordered.push(moved);
475
+ const updatedPane = { ...pane, tabs: reordered, activeTabId: existing.id };
476
+ const newLayout = updatePane(paneLayout, updatedPane);
477
+ set(syncFromLayout(newLayout));
478
+ return;
479
+ }
480
+ }
481
+ state.setActiveTab(existing.id);
482
+ return;
483
+ }
484
+
485
+ const focusedPane = findPane(paneLayout, paneLayout.focusedPaneId);
486
+ if (!focusedPane) return;
487
+
488
+ const newTab: Tab = {
489
+ id: crypto.randomUUID(),
490
+ type: 'society',
491
+ label: 'Worker 社会',
492
+ createdAt: Date.now(),
493
+ };
494
+
495
+ const updatedPane = {
496
+ ...focusedPane,
497
+ tabs: [...focusedPane.tabs, newTab],
498
+ activeTabId: newTab.id,
499
+ };
500
+ const newLayout = updatePane(paneLayout, updatedPane);
501
+ set(syncFromLayout(newLayout));
502
+ },
503
+
504
+ openChatTab: () => {
505
+ const state = get();
506
+ const focusedPane = findPane(state.paneLayout, state.paneLayout.focusedPaneId);
507
+ const existingTab = focusedPane?.tabs.find((tab) => tab.type === 'chat');
508
+ if (existingTab) {
509
+ state.setActiveTab(existingTab.id);
510
+ return;
511
+ }
512
+
513
+ state.openTab({
514
+ type: 'chat',
515
+ label: '加入飞书群',
516
+ });
517
+ },
518
+
458
519
  // Open a session report tab based on a source session tab
459
520
  openSessionReport: (sourceTabId: string) => {
460
521
  const state = get();