@yancyyu/openhermit 1.6.42 → 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-DlFQ6mai.js → ProjectEditorOverlay-C98qSs7-.js} +1 -1
  4. package/dist-renderer/assets/{TeamGraphOverlay-D2TPMPGR.js → TeamGraphOverlay-CsBbZwcL.js} +1 -1
  5. package/dist-renderer/assets/{_basePickBy-Cmd0RHLQ.js → _basePickBy-ZOyLWjMK.js} +1 -1
  6. package/dist-renderer/assets/{_baseUniq-BI_iy8ea.js → _baseUniq-DBb726rt.js} +1 -1
  7. package/dist-renderer/assets/{arc-NzW2mjTP.js → arc-CdiTaR_R.js} +1 -1
  8. package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-Bzq85AYv.js → architectureDiagram-VXUJARFQ-Cz3sc5TH.js} +1 -1
  9. package/dist-renderer/assets/{blockDiagram-VD42YOAC-D1PvYS-b.js → blockDiagram-VD42YOAC-DE4c-KJ3.js} +1 -1
  10. package/dist-renderer/assets/{c4Diagram-YG6GDRKO-D49RKzPC.js → c4Diagram-YG6GDRKO-CmTMDTrV.js} +1 -1
  11. package/dist-renderer/assets/channel-KTpqi9eT.js +1 -0
  12. package/dist-renderer/assets/{chunk-4BX2VUAB-fmI_MQmQ.js → chunk-4BX2VUAB-rhHy3tFl.js} +1 -1
  13. package/dist-renderer/assets/{chunk-55IACEB6-Xsv9RCXZ.js → chunk-55IACEB6-fLZBzuo_.js} +1 -1
  14. package/dist-renderer/assets/{chunk-B4BG7PRW-BE1KO8Um.js → chunk-B4BG7PRW-DOzxQhim.js} +1 -1
  15. package/dist-renderer/assets/{chunk-DI55MBZ5-tqJ7Mv7f.js → chunk-DI55MBZ5-COQCcXC5.js} +1 -1
  16. package/dist-renderer/assets/{chunk-FMBD7UC4-DMD45MVJ.js → chunk-FMBD7UC4-IKU9U_Y4.js} +1 -1
  17. package/dist-renderer/assets/{chunk-QN33PNHL-DOhGrz-q.js → chunk-QN33PNHL-D6WV154X.js} +1 -1
  18. package/dist-renderer/assets/{chunk-QZHKN3VN-D8yDgJdD.js → chunk-QZHKN3VN-D90_2DQp.js} +1 -1
  19. package/dist-renderer/assets/{chunk-TZMSLE5B-BcsEDu7A.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-DlSqGHMX.js → cose-bilkent-S5V4N54A-6WiK6U2P.js} +1 -1
  24. package/dist-renderer/assets/{dagre-6UL2VRFP-BTT9tSAx.js → dagre-6UL2VRFP-DF4MMuTn.js} +1 -1
  25. package/dist-renderer/assets/{diagram-PSM6KHXK-Du-U-mK2.js → diagram-PSM6KHXK-CcF1eZ7E.js} +1 -1
  26. package/dist-renderer/assets/{diagram-QEK2KX5R-jFdHeKas.js → diagram-QEK2KX5R-DYlOVPQB.js} +1 -1
  27. package/dist-renderer/assets/{diagram-S2PKOQOG-DKLNK2bu.js → diagram-S2PKOQOG-BHXWsZOP.js} +1 -1
  28. package/dist-renderer/assets/{erDiagram-Q2GNP2WA-CZxHgIIo.js → erDiagram-Q2GNP2WA-GjmuBx8d.js} +1 -1
  29. package/dist-renderer/assets/{flowDiagram-NV44I4VS-v4XStCD0.js → flowDiagram-NV44I4VS-BuS7YVHk.js} +1 -1
  30. package/dist-renderer/assets/{ganttDiagram-JELNMOA3-DJjD_BEL.js → ganttDiagram-JELNMOA3-3Teu5tAa.js} +1 -1
  31. package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-BNy-jr03.js → gitGraphDiagram-V2S2FVAM-BiLdCYu5.js} +1 -1
  32. package/dist-renderer/assets/{graph-DDTrn6je.js → graph-CDP_R8ct.js} +1 -1
  33. package/dist-renderer/assets/{index-BBp78BAu.js → index-BSZdT-g-.js} +1 -1
  34. package/dist-renderer/assets/{index-eotrJaYy.js → index-BhWvMqsz.js} +1 -1
  35. package/dist-renderer/assets/{index-D8_B-cfs.js → index-C2_AupSj.js} +1 -1
  36. package/dist-renderer/assets/{index-BQrwHZ-k.js → index-C5ujiwAR.js} +580 -588
  37. package/dist-renderer/assets/index-CIS2CTK9.css +1 -0
  38. package/dist-renderer/assets/{index-CRKQSG9S.js → index-CVNjLwkq.js} +1 -1
  39. package/dist-renderer/assets/{index-DR6Wz52b.js → index-CwG3se0q.js} +1 -1
  40. package/dist-renderer/assets/{infoDiagram-HS3SLOUP-DqnOsuza.js → infoDiagram-HS3SLOUP-DLHUFo72.js} +1 -1
  41. package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-DTobaO1d.js → journeyDiagram-XKPGCS4Q-BE07RpJD.js} +1 -1
  42. package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-HbwVOvWc.js → kanban-definition-3W4ZIXB7-DDHZy4NB.js} +1 -1
  43. package/dist-renderer/assets/{layout--VYmTcw2.js → layout-5nA5wUxO.js} +1 -1
  44. package/dist-renderer/assets/{linear-BsJh89Mr.js → linear-BtF1i2qN.js} +1 -1
  45. package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-BZqUZePd.js → mindmap-definition-VGOIOE7T-Z1Ui9Sqy.js} +1 -1
  46. package/dist-renderer/assets/{pieDiagram-ADFJNKIX-B1q_nH6P.js → pieDiagram-ADFJNKIX-LCjxckWv.js} +1 -1
  47. package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-UD8QhSEu.js → quadrantDiagram-AYHSOK5B-BOwKjSco.js} +1 -1
  48. package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-BA_i7Nw8.js → requirementDiagram-UZGBJVZJ-pChP8Znd.js} +1 -1
  49. package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-CMTnX-2d.js → sankeyDiagram-TZEHDZUN-DifZ2qpo.js} +1 -1
  50. package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-BQXDB615.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-BAsPXy6X.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-BdasmVkC.js → timeline-definition-IT6M3QCI-CPgokIo8.js} +1 -1
  55. package/dist-renderer/assets/{treemap-GDKQZRPO-BkKQqIui.js → treemap-GDKQZRPO-DAVqSR9L.js} +1 -1
  56. package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-EAlPHOdx.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 +1709 -534
  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 +679 -74
  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 +115 -37
  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 +143 -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 +156 -83
  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 +41 -5
  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-Ch7JrfUu.js +0 -1
  274. package/dist-renderer/assets/classDiagram-2ON5EDUG-z9I4AnFy.js +0 -1
  275. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-z9I4AnFy.js +0 -1
  276. package/dist-renderer/assets/clone-Dfi1Jx6l.js +0 -1
  277. package/dist-renderer/assets/index-iyjkpSus.css +0 -32
  278. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-DTUIBfce.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
@@ -1,6 +1,7 @@
1
1
  import { useEffect, useState } from 'react';
2
2
 
3
3
  import { Button } from '@renderer/components/ui/button';
4
+ import { downloadTextFile } from '../../team/CcSessionsSection';
4
5
  import { SettingRow, SettingsSectionHeader, SettingsToggle } from '../components';
5
6
  import type { TaskBusConfig } from '@shared/types/team';
6
7
  import {
@@ -25,14 +26,19 @@ interface TelemetryStatus {
25
26
  tokensOut: number;
26
27
  cacheRead: number;
27
28
  cacheCreation: number;
29
+ totalTokens: number;
28
30
  activeDays: number;
29
31
  hourly: number[];
30
32
  projects: Array<{
31
33
  cwd: string;
34
+ displayName?: string;
35
+ teamSlug?: string;
36
+ bindProject?: string;
32
37
  sessions: number;
33
38
  messages: number;
34
39
  tokensIn: number;
35
40
  tokensOut: number;
41
+ tokensTotal: number;
36
42
  }>;
37
43
  workSecondsByDay: Record<string, number>;
38
44
  }
@@ -58,20 +64,27 @@ function UsageDashboard({ status }: { status: TelemetryStatus }): React.JSX.Elem
58
64
  return (
59
65
  <div className="space-y-4 rounded-md border border-[var(--color-border)] bg-[var(--color-surface-raised)] p-4">
60
66
  <div className="flex items-center justify-between">
61
- <span className="text-xs font-medium text-[var(--color-text-muted)]">使用指标概览</span>
62
- <span className="text-[10px] text-[var(--color-text-muted)]">累计数据(全部历史)</span>
67
+ <span className="text-xs font-medium text-[var(--color-text-muted)]">Loop 使用概览</span>
68
+ <span className="text-[10px] text-[var(--color-text-muted)]">
69
+ 累计 Loop 数据(全部历史)
70
+ </span>
63
71
  </div>
64
72
  <div className="grid grid-cols-2 gap-3 sm:grid-cols-4">
65
73
  <StatCard
66
74
  icon={<MessageSquare size={14} />}
67
- label="会话"
75
+ label="采集会话"
68
76
  value={formatNum(status.sessions)}
69
77
  />
70
78
  <StatCard
71
79
  icon={<MessageSquare size={14} />}
72
- label="消息"
80
+ label="Messages"
73
81
  value={formatNum(status.messages)}
74
82
  />
83
+ <StatCard
84
+ icon={<Zap size={14} />}
85
+ label="Total Tokens"
86
+ value={formatNum(status.totalTokens)}
87
+ />
75
88
  <StatCard icon={<Zap size={14} />} label="Input" value={formatNum(status.tokensIn)} />
76
89
  <StatCard icon={<Zap size={14} />} label="Output" value={formatNum(status.tokensOut)} />
77
90
  <StatCard icon={<Calendar size={14} />} label="活跃天" value={String(status.activeDays)} />
@@ -89,14 +102,16 @@ function UsageDashboard({ status }: { status: TelemetryStatus }): React.JSX.Elem
89
102
  </div>
90
103
 
91
104
  <div>
92
- <div className="mb-2 text-xs font-medium text-[var(--color-text-muted)]">24小时分布</div>
105
+ <div className="mb-2 text-xs font-medium text-[var(--color-text-muted)]">
106
+ 24小时 Messages 分布
107
+ </div>
93
108
  <div className="flex h-16 items-end gap-0.5">
94
109
  {status.hourly.map((count, i) => {
95
110
  const pct = (count / maxHourly) * 100;
96
111
  return (
97
112
  <div
98
113
  key={i}
99
- className="flex-1 rounded-sm bg-indigo-500/60 transition-all hover:bg-indigo-500"
114
+ className="flex-1 rounded-sm bg-[var(--color-accent-muted)] transition-all hover:bg-[var(--color-accent)]"
100
115
  style={{ height: `${Math.max(pct, 2)}%` }}
101
116
  title={`${i}:00 - ${count} messages`}
102
117
  />
@@ -142,26 +157,24 @@ function UsageDashboard({ status }: { status: TelemetryStatus }): React.JSX.Elem
142
157
 
143
158
  {status.projects.length > 0 && (
144
159
  <div>
145
- <div className="mb-2 text-xs font-medium text-[var(--color-text-muted)]">
146
- 项目排行(累计)
147
- </div>
160
+ <div className="mb-2 text-xs font-medium text-[var(--color-text-muted)]">项目吞吐</div>
148
161
  {/* Header row */}
149
- <div className="grid grid-cols-[1fr_64px_64px] items-center gap-2 pb-1 text-[10px] text-[var(--color-text-muted)]">
150
- <span>项目</span>
151
- <span className="text-right">消息</span>
152
- <span className="text-right">Token</span>
162
+ <div className="grid grid-cols-[1fr_72px_80px] items-center gap-2 pb-1 text-[10px] text-[var(--color-text-muted)]">
163
+ <span>名称</span>
164
+ <span className="text-right">Messages</span>
165
+ <span className="text-right">Total Tokens</span>
153
166
  </div>
154
167
  <div className="max-h-40 space-y-1 overflow-y-auto">
155
168
  {status.projects.slice(0, 10).map((proj, i) => (
156
- <div key={i} className="grid grid-cols-[1fr_64px_64px] items-center gap-2 text-xs">
169
+ <div key={i} className="grid grid-cols-[1fr_72px_80px] items-center gap-2 text-xs">
157
170
  <span className="truncate text-[var(--color-text-secondary)]" title={proj.cwd}>
158
- {proj.cwd.split('/').pop() || proj.cwd}
171
+ {proj.displayName || proj.cwd.split('/').pop() || proj.cwd}
159
172
  </span>
160
173
  <span className="text-right text-[var(--color-text-muted)]">
161
174
  {formatNum(proj.messages)}
162
175
  </span>
163
176
  <span className="text-right text-[var(--color-text-muted)]">
164
- {formatNum(proj.tokensIn + proj.tokensOut)}
177
+ {formatNum(proj.tokensTotal)}
165
178
  </span>
166
179
  </div>
167
180
  ))}
@@ -207,6 +220,7 @@ export function TaskBusSection(): React.JSX.Element {
207
220
  const [collaborationEnabled, setCollaborationEnabled] = useState(false);
208
221
  const [telemetryPlatform, setTelemetryPlatform] = useState('claudecode');
209
222
  const [scanning, setScanning] = useState(false);
223
+ const [exporting, setExporting] = useState(false);
210
224
  const [telemetryStatus, setTelemetryStatus] = useState<TelemetryStatus | null>(null);
211
225
 
212
226
  useEffect(() => {
@@ -377,6 +391,23 @@ export function TaskBusSection(): React.JSX.Element {
377
391
  .finally(() => setScanning(false));
378
392
  };
379
393
 
394
+ const exportTelemetry = () => {
395
+ if (exporting) return;
396
+ setExporting(true);
397
+ fetch('/api/telemetry/export?format=csv')
398
+ .then((r) => r.json())
399
+ .then((payload: { filename?: string; mimeType?: string; content?: string }) => {
400
+ if (payload.filename && payload.mimeType && payload.content !== undefined) {
401
+ downloadTextFile(payload.content, payload.filename, payload.mimeType);
402
+ setMessage('采集数据已导出');
403
+ } else {
404
+ setMessage('导出失败:没有可导出的数据');
405
+ }
406
+ })
407
+ .catch(() => setMessage('导出失败,请稍后重试'))
408
+ .finally(() => setExporting(false));
409
+ };
410
+
380
411
  const saveTelemetryPlatform = (nextPlatform = telemetryPlatform) => {
381
412
  const config = buildConfig({ telemetryPlatform: nextPlatform });
382
413
  fetch('/api/settings/task-bus', {
@@ -412,7 +443,7 @@ export function TaskBusSection(): React.JSX.Element {
412
443
 
413
444
  <SettingRow
414
445
  label="数据采集"
415
- description="扫描本机 ~/.claude/projects 会话文件,采集使用指标;不需要 Redis,也不会上传对话内容"
446
+ description="扫描本机 ~/.claude/projects 会话文件,采集 Loop 使用指标;不需要 Redis,也不会上传对话内容"
416
447
  >
417
448
  <div className="flex items-center gap-2">
418
449
  <select
@@ -422,7 +453,7 @@ export function TaskBusSection(): React.JSX.Element {
422
453
  setTelemetryPlatform(nextPlatform);
423
454
  saveTelemetryPlatform(nextPlatform);
424
455
  }}
425
- className="rounded-md border border-[var(--color-border)] bg-[var(--color-bg)] px-2 py-1 text-xs outline-none focus:border-indigo-500/50"
456
+ className="rounded-md border border-[var(--color-border)] bg-[var(--color-bg)] px-2 py-1 text-xs outline-none focus:border-[var(--color-accent-border)]"
426
457
  >
427
458
  <option value="claudecode">Claude Code</option>
428
459
  </select>
@@ -447,6 +478,16 @@ export function TaskBusSection(): React.JSX.Element {
447
478
  {scanning ? <Loader2 size={12} className="animate-spin" /> : <BarChart3 size={12} />}
448
479
  {scanning ? '采集中...' : '立即采集'}
449
480
  </Button>
481
+ <Button
482
+ size="sm"
483
+ variant="outline"
484
+ onClick={exportTelemetry}
485
+ disabled={exporting || !telemetryStatus}
486
+ className="gap-1.5"
487
+ >
488
+ {exporting ? <Loader2 size={12} className="animate-spin" /> : <BarChart3 size={12} />}
489
+ 导出 CSV
490
+ </Button>
450
491
  <span className="text-[10px] text-[var(--color-text-muted)]">
451
492
  本地扫描,不依赖团队总线或 Redis。{!collectionEnabled ? '开启数据采集后可手动刷新。' : ''}
452
493
  </span>
@@ -457,7 +498,7 @@ export function TaskBusSection(): React.JSX.Element {
457
498
  <UsageDashboard status={telemetryStatus} />
458
499
  ) : (
459
500
  <div className="rounded-md border border-[var(--color-border)] bg-[var(--color-surface-raised)] p-4 text-xs text-[var(--color-text-muted)]">
460
- 使用指标概览加载中;开启数据采集后会扫描本机 Claude Code 会话文件。
501
+ Loop 使用概览加载中;开启数据采集后会扫描本机 Claude Code 会话文件。
461
502
  </div>
462
503
  )}
463
504
  </div>
@@ -507,7 +548,7 @@ export function TaskBusSection(): React.JSX.Element {
507
548
  setHost(e.target.value);
508
549
  setConnected(false);
509
550
  }}
510
- className="w-full rounded-md border border-[var(--color-border)] bg-[var(--color-bg)] px-2.5 py-1.5 text-sm outline-none focus:border-indigo-500/50 focus:ring-1 focus:ring-indigo-500/30"
551
+ className="w-full rounded-md border border-[var(--color-border)] bg-[var(--color-bg)] px-2.5 py-1.5 text-sm outline-none focus:border-[var(--color-accent-border)] focus:ring-1 focus:ring-[var(--color-accent-border)]"
511
552
  placeholder="127.0.0.1"
512
553
  />
513
554
  </div>
@@ -520,7 +561,7 @@ export function TaskBusSection(): React.JSX.Element {
520
561
  setPort(Number(e.target.value));
521
562
  setConnected(false);
522
563
  }}
523
- className="w-full rounded-md border border-[var(--color-border)] bg-[var(--color-bg)] px-2.5 py-1.5 text-sm outline-none focus:border-indigo-500/50 focus:ring-1 focus:ring-indigo-500/30"
564
+ className="w-full rounded-md border border-[var(--color-border)] bg-[var(--color-bg)] px-2.5 py-1.5 text-sm outline-none focus:border-[var(--color-accent-border)] focus:ring-1 focus:ring-[var(--color-accent-border)]"
524
565
  placeholder="6379"
525
566
  />
526
567
  </div>
@@ -534,7 +575,7 @@ export function TaskBusSection(): React.JSX.Element {
534
575
  setPassword(e.target.value);
535
576
  setConnected(false);
536
577
  }}
537
- className="w-full rounded-md border border-[var(--color-border)] bg-[var(--color-bg)] px-2.5 py-1.5 text-sm outline-none focus:border-indigo-500/50 focus:ring-1 focus:ring-indigo-500/30"
578
+ className="w-full rounded-md border border-[var(--color-border)] bg-[var(--color-bg)] px-2.5 py-1.5 text-sm outline-none focus:border-[var(--color-accent-border)] focus:ring-1 focus:ring-[var(--color-accent-border)]"
538
579
  placeholder="可选"
539
580
  />
540
581
  </div>
@@ -39,6 +39,18 @@ const PAGE_SIZE = 8;
39
39
  const REFRESH_INTERVAL_MS = 2000;
40
40
  const SESSION_DETAIL_PAGE_SIZE = 50;
41
41
 
42
+ function sessionExpansionKey(teamName: string, sessionId: string): string {
43
+ return `${teamName}\0${sessionId}`;
44
+ }
45
+
46
+ function formatSessionDetailError(error: unknown): string {
47
+ const message = error instanceof Error ? error.message : String(error || '');
48
+ if (/session not found/i.test(message)) {
49
+ return '会话文件已不存在,请刷新会话列表';
50
+ }
51
+ return message || '加载会话历史失败';
52
+ }
53
+
42
54
  interface TaggedSession extends CcSession {
43
55
  teamName: string;
44
56
  teamDisplayName: string;
@@ -55,11 +67,11 @@ export const SidebarSessions = (): React.JSX.Element => {
55
67
  const [error, setError] = useState<string | null>(null);
56
68
  const [visibleCount, setVisibleCount] = useState(PAGE_SIZE);
57
69
  const [searchQuery, setSearchQuery] = useState('');
58
- const [cancellingId, setCancellingId] = useState<string | null>(null);
70
+ const [cancellingKey, setCancellingKey] = useState<string | null>(null);
59
71
  const [exporting, setExporting] = useState(false);
60
72
  const [telemetryRows, setTelemetryRows] = useState<ConversationTelemetryRow[]>([]);
61
73
  const [loadingTelemetry, setLoadingTelemetry] = useState(false);
62
- const [expandedId, setExpandedId] = useState<string | null>(null);
74
+ const [expandedKey, setExpandedKey] = useState<string | null>(null);
63
75
  const refreshInFlightRef = useRef(false);
64
76
  const needsRefreshRef = useRef(false);
65
77
 
@@ -115,7 +127,9 @@ export const SidebarSessions = (): React.JSX.Element => {
115
127
  if (r.status === 'fulfilled') merged.push(...r.value);
116
128
  }
117
129
  setAllSessions(merged);
118
- setExpandedId((prev) => (prev && merged.some((s) => s.id === prev) ? prev : null));
130
+ setExpandedKey((prev) =>
131
+ prev && merged.some((s) => sessionExpansionKey(s.teamName, s.id) === prev) ? prev : null
132
+ );
119
133
  } catch (err) {
120
134
  setError(err instanceof Error ? err.message : '加载失败');
121
135
  } finally {
@@ -185,7 +199,7 @@ export const SidebarSessions = (): React.JSX.Element => {
185
199
 
186
200
  useEffect(() => {
187
201
  setVisibleCount(PAGE_SIZE);
188
- setExpandedId(null);
202
+ setExpandedKey(null);
189
203
  }, [scopedTeamName]);
190
204
 
191
205
  useEffect(() => {
@@ -227,26 +241,28 @@ export const SidebarSessions = (): React.JSX.Element => {
227
241
 
228
242
  const handleCancel = useCallback(
229
243
  async (teamName: string, sessionId: string) => {
230
- setCancellingId(sessionId);
244
+ const key = sessionExpansionKey(teamName, sessionId);
245
+ setCancellingKey(key);
231
246
  try {
232
247
  await api.teams.cancelSession(teamName, sessionId);
233
248
  // Remove from list immediately
234
- setAllSessions((prev) => prev.filter((s) => s.id !== sessionId));
249
+ setAllSessions((prev) => prev.filter((s) => sessionExpansionKey(s.teamName, s.id) !== key));
235
250
  // Clear expanded state if this session was expanded
236
- if (expandedId === sessionId) {
237
- setExpandedId(null);
251
+ if (expandedKey === key) {
252
+ setExpandedKey(null);
238
253
  }
239
254
  } catch (err) {
240
255
  console.error('Failed to close session:', err);
241
256
  } finally {
242
- setCancellingId(null);
257
+ setCancellingKey(null);
243
258
  }
244
259
  },
245
- [expandedId]
260
+ [expandedKey]
246
261
  );
247
262
 
248
263
  const handleExpand = useCallback((teamName: string, sessionId: string) => {
249
- setExpandedId((prev) => (prev === sessionId ? null : sessionId));
264
+ const key = sessionExpansionKey(teamName, sessionId);
265
+ setExpandedKey((prev) => (prev === key ? null : key));
250
266
  }, []);
251
267
 
252
268
  const handleExportAllSessions = useCallback(async () => {
@@ -425,12 +441,12 @@ export const SidebarSessions = (): React.JSX.Element => {
425
441
  .filter((s) => s.live)
426
442
  .map((s) => (
427
443
  <SessionRow
428
- key={s.id}
444
+ key={sessionExpansionKey(s.teamName, s.id)}
429
445
  session={s}
430
446
  onCancel={handleCancel}
431
447
  onExpand={handleExpand}
432
- isExpanded={expandedId === s.id}
433
- cancelling={cancellingId === s.id}
448
+ isExpanded={expandedKey === sessionExpansionKey(s.teamName, s.id)}
449
+ cancelling={cancellingKey === sessionExpansionKey(s.teamName, s.id)}
434
450
  />
435
451
  ))}
436
452
  </>
@@ -446,11 +462,11 @@ export const SidebarSessions = (): React.JSX.Element => {
446
462
  .filter((s) => !s.live)
447
463
  .map((s) => (
448
464
  <SessionRow
449
- key={s.id}
465
+ key={sessionExpansionKey(s.teamName, s.id)}
450
466
  session={s}
451
467
  onCancel={handleCancel}
452
468
  onExpand={handleExpand}
453
- isExpanded={expandedId === s.id}
469
+ isExpanded={expandedKey === sessionExpansionKey(s.teamName, s.id)}
454
470
  cancelling={false}
455
471
  />
456
472
  ))}
@@ -492,6 +508,7 @@ const SessionRow = ({
492
508
  const platformLabel = session.platform === 'bridge' ? 'Bridge' : session.platform;
493
509
  const [detail, setDetail] = useState<CcSessionDetail | null>(null);
494
510
  const [loadingDetail, setLoadingDetail] = useState(false);
511
+ const [detailError, setDetailError] = useState<string | null>(null);
495
512
  const [historyLimit, setHistoryLimit] = useState(SESSION_DETAIL_PAGE_SIZE);
496
513
  const [loadingMoreHistory, setLoadingMoreHistory] = useState(false);
497
514
  const hasMoreHistory =
@@ -501,8 +518,9 @@ const SessionRow = ({
501
518
 
502
519
  // Fetch detail when expanded
503
520
  useEffect(() => {
504
- if (!isExpanded) {
521
+ if (!isExpanded || session.hasLocalFile === false) {
505
522
  setDetail(null);
523
+ setDetailError(null);
506
524
  setLoadingDetail(false);
507
525
  setLoadingMoreHistory(false);
508
526
  setHistoryLimit(SESSION_DETAIL_PAGE_SIZE);
@@ -510,14 +528,18 @@ const SessionRow = ({
510
528
  }
511
529
  let cancelled = false;
512
530
  setLoadingDetail(true);
531
+ setDetailError(null);
513
532
  const isIncrementalLoad = historyLimit > SESSION_DETAIL_PAGE_SIZE;
514
533
  setLoadingMoreHistory(isIncrementalLoad);
515
534
  void (async () => {
516
535
  try {
517
536
  const d = await api.teams.getSessionDetail(session.teamName, session.id, historyLimit);
518
537
  if (!cancelled) setDetail(d);
519
- } catch {
520
- if (!cancelled) setDetail(null);
538
+ } catch (err) {
539
+ if (!cancelled) {
540
+ setDetail(null);
541
+ setDetailError(formatSessionDetailError(err));
542
+ }
521
543
  } finally {
522
544
  if (!cancelled) {
523
545
  setLoadingDetail(false);
@@ -534,7 +556,7 @@ const SessionRow = ({
534
556
  // refetch (no skeleton flash) so newly arrived messages show without needing
535
557
  // to collapse and reopen.
536
558
  useEffect(() => {
537
- if (!isExpanded || !session.live) {
559
+ if (!isExpanded || !session.live || session.hasLocalFile === false) {
538
560
  return;
539
561
  }
540
562
  const intervalId = window.setInterval(() => {
@@ -557,7 +579,7 @@ const SessionRow = ({
557
579
  // this session's team, refresh the detail right away instead of waiting for
558
580
  // the next polling cycle. This makes agent replies appear in <100ms.
559
581
  useEffect(() => {
560
- if (!isExpanded) {
582
+ if (!isExpanded || session.hasLocalFile === false) {
561
583
  return;
562
584
  }
563
585
  const unsubscribe = api.teams.onTeamChange?.((_event, change: TeamChangeEvent) => {
@@ -591,7 +613,7 @@ const SessionRow = ({
591
613
  useEffect(() => {
592
614
  const wasLive = prevLiveRef.current;
593
615
  prevLiveRef.current = session.live;
594
- if (wasLive && !session.live && isExpanded) {
616
+ if (wasLive && !session.live && isExpanded && session.hasLocalFile !== false) {
595
617
  void (async () => {
596
618
  try {
597
619
  const d = await api.teams.getSessionDetail(session.teamName, session.id, historyLimit);
@@ -689,69 +711,87 @@ const SessionRow = ({
689
711
  {/* Inline expanded messages */}
690
712
  {isExpanded && (
691
713
  <div className="ml-5 border-l-2 border-[var(--color-border)]">
692
- {loadingDetail && !detail && (
693
- <div className="px-3 py-3">
694
- <div className="space-y-2">
695
- {[1, 2, 3].map((i) => (
696
- <div
697
- key={i}
698
- className="h-3 animate-pulse rounded bg-[var(--color-surface-raised)]"
699
- />
700
- ))}
701
- </div>
714
+ {session.hasLocalFile === false ? (
715
+ // cc-only session (e.g. Feishu listening, no local JSONL yet) —
716
+ // no local history. Avoids the misleading "会话文件已不存在" 404
717
+ // path (#20). Same root cause as CcSessionsSection.
718
+ <div className="flex items-center gap-2 px-3 py-2 text-xs text-[var(--color-text-muted)]">
719
+ <Radio size={13} className="shrink-0 animate-pulse text-emerald-400" />
720
+ <span>监听中,暂无本地历史</span>
702
721
  </div>
703
- )}
704
- {detail && (
722
+ ) : (
705
723
  <>
706
- {detail.history.length === 0 ? (
707
- <div className="px-3 py-3 text-xs text-[var(--color-text-muted)]">暂无消息</div>
708
- ) : (
724
+ {loadingDetail && !detail && (
725
+ <div className="px-3 py-3">
726
+ <div className="space-y-2">
727
+ {[1, 2, 3].map((i) => (
728
+ <div
729
+ key={i}
730
+ className="h-3 animate-pulse rounded bg-[var(--color-surface-raised)]"
731
+ />
732
+ ))}
733
+ </div>
734
+ </div>
735
+ )}
736
+ {detailError && !loadingDetail && (
737
+ <div className="flex items-center gap-2 px-3 py-2 text-xs text-red-400">
738
+ <AlertCircle size={13} className="shrink-0" />
739
+ <span>{detailError}</span>
740
+ </div>
741
+ )}
742
+ {detail && (
709
743
  <>
710
- <div className="divide-[var(--color-border)]/50 max-h-64 divide-y overflow-y-auto">
711
- {[...detail.history].reverse().map((msg, i) => (
712
- <div key={i} className="px-3 py-2 text-[11px]">
713
- <div className="flex items-center gap-2">
714
- <span
715
- className={`shrink-0 text-[10px] font-medium ${
716
- msg.role === 'user'
717
- ? 'text-indigo-400'
718
- : 'text-[var(--color-text-muted)]'
719
- }`}
744
+ {detail.history.length === 0 ? (
745
+ <div className="px-3 py-3 text-xs text-[var(--color-text-muted)]">暂无消息</div>
746
+ ) : (
747
+ <>
748
+ <div className="divide-[var(--color-border)]/50 max-h-64 divide-y overflow-y-auto">
749
+ {[...detail.history].reverse().map((msg, i) => (
750
+ <div key={i} className="px-3 py-2 text-[11px]">
751
+ <div className="flex items-center gap-2">
752
+ <span
753
+ className={`shrink-0 text-[10px] font-medium ${
754
+ msg.role === 'user'
755
+ ? 'text-indigo-400'
756
+ : 'text-[var(--color-text-muted)]'
757
+ }`}
758
+ >
759
+ {msg.role === 'user' ? '用户' : 'Agent'}
760
+ </span>
761
+ <span className="text-[10px] text-[var(--color-text-muted)] opacity-60">
762
+ {formatMessageTime(msg.timestamp)}
763
+ </span>
764
+ </div>
765
+ <div className="mt-1 whitespace-pre-wrap break-words text-[var(--color-text)]">
766
+ {msg.content.slice(0, 500)}
767
+ {msg.content.length > 500 ? '…' : ''}
768
+ </div>
769
+ </div>
770
+ ))}
771
+ </div>
772
+ {hasMoreHistory && (
773
+ <div className="border-[var(--color-border)]/50 border-t px-3 py-2">
774
+ <button
775
+ type="button"
776
+ className="inline-flex items-center gap-1 text-xs text-[var(--color-text-muted)] transition-colors hover:text-[var(--color-text)] disabled:cursor-not-allowed disabled:opacity-50"
777
+ onClick={handleLoadMoreHistory}
778
+ disabled={loadingDetail || loadingMoreHistory}
720
779
  >
721
- {msg.role === 'user' ? '用户' : 'Agent'}
722
- </span>
723
- <span className="text-[10px] text-[var(--color-text-muted)] opacity-60">
724
- {formatMessageTime(msg.timestamp)}
725
- </span>
726
- </div>
727
- <div className="mt-1 whitespace-pre-wrap break-words text-[var(--color-text)]">
728
- {msg.content.slice(0, 500)}
729
- {msg.content.length > 500 ? '…' : ''}
780
+ {loadingMoreHistory ? (
781
+ <>
782
+ <Loader2 size={12} className="animate-spin" />
783
+ 正在加载更早消息...
784
+ </>
785
+ ) : (
786
+ <>
787
+ 加载更早消息 (
788
+ {Math.max(detail.historyCount - detail.history.length, 0)} 条)
789
+ </>
790
+ )}
791
+ </button>
730
792
  </div>
731
- </div>
732
- ))}
733
- </div>
734
- {hasMoreHistory && (
735
- <div className="border-[var(--color-border)]/50 border-t px-3 py-2">
736
- <button
737
- type="button"
738
- className="inline-flex items-center gap-1 text-xs text-[var(--color-text-muted)] transition-colors hover:text-[var(--color-text)] disabled:cursor-not-allowed disabled:opacity-50"
739
- onClick={handleLoadMoreHistory}
740
- disabled={loadingDetail || loadingMoreHistory}
741
- >
742
- {loadingMoreHistory ? (
743
- <>
744
- <Loader2 size={12} className="animate-spin" />
745
- 正在加载更早消息...
746
- </>
747
- ) : (
748
- <>
749
- 加载更早消息 ({Math.max(detail.historyCount - detail.history.length, 0)}{' '}
750
- 条)
751
- </>
752
- )}
753
- </button>
754
- </div>
793
+ )}
794
+ </>
755
795
  )}
756
796
  </>
757
797
  )}
@@ -145,7 +145,7 @@ export const SidebarTaskItem = ({
145
145
 
146
146
  const showTeamRow = showTeamName && !hideTeamName;
147
147
  const unreadBackgroundClass =
148
- unreadCount > 0 ? (isLight ? 'bg-indigo-500/[0.03]' : 'bg-indigo-500/[0.05]') : '';
148
+ unreadCount > 0 ? (isLight ? 'bg-blue-500/[0.03]' : 'bg-blue-500/[0.05]') : '';
149
149
 
150
150
  return (
151
151
  <button
@@ -21,8 +21,11 @@ declare global {
21
21
  }
22
22
  }
23
23
 
24
+ // Version is injected at build time from package.json (vite.web.config.ts define),
25
+ // the same source the Settings page and Sentry use — so the splash version always
26
+ // tracks the real release instead of a stale hardcoded literal.
24
27
  const BOOT_LINES = [
25
- '🦀 hermit v1.6.38',
28
+ `🦀 hermit v${__APP_VERSION__}`,
26
29
  'connecting harness…',
27
30
  'loading team configs…',
28
31
  'scanning session history…',
@@ -96,7 +99,8 @@ export function startSplashScene(
96
99
  if (!line) return;
97
100
 
98
101
  const lineEl = document.createElement('div');
99
- lineEl.style.cssText = 'opacity: 0; transform: translateY(4px); transition: opacity 0.3s, transform 0.3s;';
102
+ lineEl.style.cssText =
103
+ 'opacity: 0; transform: translateY(4px); transition: opacity 0.3s, transform 0.3s;';
100
104
 
101
105
  if (lineIndex === 0) {
102
106
  // First line — brand with crab