@yancyyu/openhermit 1.6.28 → 1.6.30

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 (177) hide show
  1. package/dist-renderer/assets/ProjectEditorOverlay-DsQt4FHy.js +52 -0
  2. package/dist-renderer/assets/{TeamGraphOverlay-Ba5njic5.js → TeamGraphOverlay-BjZC53xf.js} +1 -1
  3. package/dist-renderer/assets/{_basePickBy-BvnK-OC1.js → _basePickBy-CrWocIjq.js} +1 -1
  4. package/dist-renderer/assets/{_baseUniq-DmFYXx9G.js → _baseUniq-B6d8ysWi.js} +1 -1
  5. package/dist-renderer/assets/{arc-DX4ZQFY4.js → arc-DAIYCFP8.js} +1 -1
  6. package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-DfYr3vEN.js → architectureDiagram-VXUJARFQ-B3UudXJh.js} +1 -1
  7. package/dist-renderer/assets/{blockDiagram-VD42YOAC-DuXdVeWn.js → blockDiagram-VD42YOAC-DbptKQ4W.js} +1 -1
  8. package/dist-renderer/assets/{c4Diagram-YG6GDRKO-Bw2nixXe.js → c4Diagram-YG6GDRKO-C4WQuZpV.js} +1 -1
  9. package/dist-renderer/assets/channel-DbjZvWii.js +1 -0
  10. package/dist-renderer/assets/{chunk-4BX2VUAB-DLiNGQoE.js → chunk-4BX2VUAB-Dp7fVpI_.js} +1 -1
  11. package/dist-renderer/assets/{chunk-55IACEB6-B1L_8VIF.js → chunk-55IACEB6-B8KGfbAy.js} +1 -1
  12. package/dist-renderer/assets/{chunk-B4BG7PRW-DaZMWKGk.js → chunk-B4BG7PRW-BG1oJrjA.js} +1 -1
  13. package/dist-renderer/assets/{chunk-DI55MBZ5-ku-dflJG.js → chunk-DI55MBZ5-DRmxNjht.js} +1 -1
  14. package/dist-renderer/assets/{chunk-FMBD7UC4-DV-mF1dP.js → chunk-FMBD7UC4-D6VLvy16.js} +1 -1
  15. package/dist-renderer/assets/{chunk-QN33PNHL-ByGcDFQ0.js → chunk-QN33PNHL-DZou1667.js} +1 -1
  16. package/dist-renderer/assets/{chunk-QZHKN3VN-7dv-Min8.js → chunk-QZHKN3VN-CghmasSh.js} +1 -1
  17. package/dist-renderer/assets/{chunk-TZMSLE5B-WdXL5fTu.js → chunk-TZMSLE5B-B7apcMPK.js} +1 -1
  18. package/dist-renderer/assets/classDiagram-2ON5EDUG-D_FGxxsl.js +1 -0
  19. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-D_FGxxsl.js +1 -0
  20. package/dist-renderer/assets/clone-CJ1kxO2J.js +1 -0
  21. package/dist-renderer/assets/{cose-bilkent-S5V4N54A-CNcsvqPl.js → cose-bilkent-S5V4N54A-05e5uQDp.js} +1 -1
  22. package/dist-renderer/assets/{dagre-6UL2VRFP-DBNx4qqx.js → dagre-6UL2VRFP-B06bRykF.js} +1 -1
  23. package/dist-renderer/assets/{diagram-PSM6KHXK-BfVlT6sT.js → diagram-PSM6KHXK-CY7VYQ7c.js} +1 -1
  24. package/dist-renderer/assets/{diagram-QEK2KX5R-HvVjs0K6.js → diagram-QEK2KX5R-BjKEH7dD.js} +1 -1
  25. package/dist-renderer/assets/{diagram-S2PKOQOG-DYb_KnWS.js → diagram-S2PKOQOG-Bf4ELS1_.js} +1 -1
  26. package/dist-renderer/assets/{erDiagram-Q2GNP2WA-Ba-IgI5G.js → erDiagram-Q2GNP2WA-DJ753_L9.js} +1 -1
  27. package/dist-renderer/assets/{flowDiagram-NV44I4VS-2iDN8Kpj.js → flowDiagram-NV44I4VS-B71S-lC-.js} +1 -1
  28. package/dist-renderer/assets/{ganttDiagram-JELNMOA3-Byjf8Fa3.js → ganttDiagram-JELNMOA3-C_U42mSZ.js} +1 -1
  29. package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-DbKvfZ_j.js → gitGraphDiagram-V2S2FVAM-DKUJU4Ns.js} +1 -1
  30. package/dist-renderer/assets/{graph-Enirf-f8.js → graph-DY3qbzqj.js} +1 -1
  31. package/dist-renderer/assets/{index-DY1zqsb6.js → index-BlOrAXp3.js} +551 -537
  32. package/dist-renderer/assets/{index-AjxP_rE_.js → index-Bs27J5gB.js} +1 -1
  33. package/dist-renderer/assets/{index-CtlzGepK.js → index-C8B_nKOF.js} +1 -1
  34. package/dist-renderer/assets/index-CmZPUEhS.css +1 -0
  35. package/dist-renderer/assets/{index-COZPUWJW.js → index-DLKyDr4T.js} +1 -1
  36. package/dist-renderer/assets/{index-DdhqolqE.js → index-Dhsk3_DD.js} +1 -1
  37. package/dist-renderer/assets/{index-ChR1D6ZF.js → index-GpUvV2xs.js} +1 -1
  38. package/dist-renderer/assets/{infoDiagram-HS3SLOUP-D6uicwz1.js → infoDiagram-HS3SLOUP-BNs0y3IG.js} +1 -1
  39. package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-DqwZsXlQ.js → journeyDiagram-XKPGCS4Q-CqPnw4UV.js} +1 -1
  40. package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-fCDVhVUm.js → kanban-definition-3W4ZIXB7-SLlzcUJ2.js} +1 -1
  41. package/dist-renderer/assets/{layout-CPFgj98r.js → layout-BZLlNmbr.js} +1 -1
  42. package/dist-renderer/assets/{linear-CYiQ7Y3M.js → linear-qz6v45xy.js} +1 -1
  43. package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-D31dS2KE.js → mindmap-definition-VGOIOE7T-B1-kmEWV.js} +1 -1
  44. package/dist-renderer/assets/{pieDiagram-ADFJNKIX-BOsCJfds.js → pieDiagram-ADFJNKIX-B8a02iNx.js} +1 -1
  45. package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-CYTVQCfr.js → quadrantDiagram-AYHSOK5B-BKv1Xfou.js} +1 -1
  46. package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-CODCFpkt.js → requirementDiagram-UZGBJVZJ-B3DUpZi2.js} +1 -1
  47. package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-Z4ce9ZtZ.js → sankeyDiagram-TZEHDZUN-DmPzuTsy.js} +1 -1
  48. package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-CmS9TxhW.js → sequenceDiagram-WL72ISMW-Bo7RelRb.js} +1 -1
  49. package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-o9k-ns3q.js → stateDiagram-FKZM4ZOC-1epX98gV.js} +1 -1
  50. package/dist-renderer/assets/{stateDiagram-v2-4FDKWEC3-CxHMyEt1.js → stateDiagram-v2-4FDKWEC3-03Ym9PTr.js} +1 -1
  51. package/dist-renderer/assets/{timeline-definition-IT6M3QCI-B6T3zrde.js → timeline-definition-IT6M3QCI-r6isC62H.js} +1 -1
  52. package/dist-renderer/assets/{treemap-GDKQZRPO-CVd5GNDw.js → treemap-GDKQZRPO-CGKpOUF2.js} +1 -1
  53. package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-CleBrdqc.js → xychartDiagram-PRI3JC2R-t4-rwdAw.js} +1 -1
  54. package/dist-renderer/index.html +2 -2
  55. package/package.json +4 -1
  56. package/src/main/ipc/extensions.ts +353 -0
  57. package/src/main/server.ts +907 -184
  58. package/src/main/services/extensions/ExtensionFacadeService.ts +135 -0
  59. package/src/main/services/extensions/catalog/GlamaMcpEnrichmentService.ts +190 -0
  60. package/src/main/services/extensions/catalog/McpCatalogAggregator.ts +150 -0
  61. package/src/main/services/extensions/catalog/OfficialMcpRegistryService.ts +381 -0
  62. package/src/main/services/extensions/catalog/PluginCatalogService.ts +392 -0
  63. package/src/main/services/extensions/credentials/CredentialService.ts +343 -0
  64. package/src/main/services/extensions/install/McpInstallService.ts +407 -0
  65. package/src/main/services/extensions/install/PluginInstallService.ts +198 -0
  66. package/src/main/services/extensions/runtime/ClaudeCodeAdapter.ts +199 -0
  67. package/src/main/services/extensions/runtime/CodexAdapter.ts +100 -0
  68. package/src/main/services/extensions/runtime/CursorAdapter.ts +154 -0
  69. package/src/main/services/extensions/runtime/ExtensionsRuntimeAdapter.ts +172 -0
  70. package/src/main/services/extensions/runtime/GeminiAdapter.ts +91 -0
  71. package/src/main/services/extensions/runtime/HarnessInstallAdapter.ts +49 -0
  72. package/src/main/services/extensions/runtime/McpConfigStateReader.ts +209 -0
  73. package/src/main/services/extensions/runtime/OpenCodeAdapter.ts +91 -0
  74. package/src/main/services/extensions/runtime/adapterRegistry.ts +54 -0
  75. package/src/main/services/extensions/runtime/mcpDiagnosticsParser.ts +214 -0
  76. package/src/main/services/extensions/runtime/mcpRuntimeJson.ts +45 -0
  77. package/src/main/services/extensions/skills/SkillImportService.ts +155 -0
  78. package/src/main/services/extensions/skills/SkillMetadataParser.ts +323 -0
  79. package/src/main/services/extensions/skills/SkillPlanService.ts +411 -0
  80. package/src/main/services/extensions/skills/SkillReviewService.ts +73 -0
  81. package/src/main/services/extensions/skills/SkillRootsResolver.ts +49 -0
  82. package/src/main/services/extensions/skills/SkillScaffoldService.ts +89 -0
  83. package/src/main/services/extensions/skills/SkillScanner.ts +117 -0
  84. package/src/main/services/extensions/skills/SkillValidator.ts +69 -0
  85. package/src/main/services/extensions/skills/SkillsCatalogService.ts +92 -0
  86. package/src/main/services/extensions/skills/SkillsMutationService.ts +146 -0
  87. package/src/main/services/extensions/skills/SkillsWatcherService.ts +134 -0
  88. package/src/main/services/extensions/state/McpInstallationStateService.ts +42 -0
  89. package/src/main/services/extensions/state/PluginInstallationStateService.ts +281 -0
  90. package/src/main/services/identity/AgentTeamsIdentityStore.ts +218 -0
  91. package/src/main/services/runtime/providerAwareCliEnv.ts +60 -0
  92. package/src/main/services/session-intelligence/UsageTelemetryService.ts +33 -18
  93. package/src/main/services/team/ClaudeBinaryResolver.ts +469 -0
  94. package/src/main/services/team/ClaudeDoctorProbe.ts +0 -0
  95. package/src/main/services/team/cliFlavor.ts +54 -0
  96. package/src/main/services/teams-mvp/CollaborationBoardService.ts +310 -0
  97. package/src/main/services/teams-mvp/TaskDispatchService.ts +883 -95
  98. package/src/main/services/teams-mvp/TeamProvisioningService.ts +58 -19
  99. package/src/main/services/teams-mvp/TeamWorkspaceService.ts +25 -2
  100. package/src/main/services/teams-mvp/index.ts +3 -0
  101. package/src/main/utils/atomicWrite.ts +72 -0
  102. package/src/main/utils/childProcess.ts +554 -0
  103. package/src/main/utils/cliEnv.ts +54 -0
  104. package/src/main/utils/cliPathMerge.ts +97 -0
  105. package/src/main/utils/pathDecoder.ts +664 -0
  106. package/src/main/utils/pathValidation.ts +432 -0
  107. package/src/main/utils/shellEnv.ts +331 -0
  108. package/src/renderer/App.tsx +5 -0
  109. package/src/renderer/api/httpClient.ts +128 -0
  110. package/src/renderer/components/extensions/ExtensionStoreView.tsx +59 -34
  111. package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +1 -1
  112. package/src/renderer/components/extensions/common/ExtensionToast.tsx +141 -0
  113. package/src/renderer/components/extensions/common/HarnessSelector.tsx +71 -0
  114. package/src/renderer/components/extensions/env/EnvVarPanel.tsx +335 -0
  115. package/src/renderer/components/extensions/env/ProjectEnvPanel.tsx +239 -0
  116. package/src/renderer/components/extensions/mcp/CustomMcpServerDialog.tsx +14 -223
  117. package/src/renderer/components/extensions/mcp/McpServerDetailDialog.tsx +11 -0
  118. package/src/renderer/components/extensions/mcp/McpServersPanel.tsx +51 -1
  119. package/src/renderer/components/extensions/skills/SkillsPanel.tsx +1 -126
  120. package/src/renderer/components/layout/PaneContent.tsx +2 -0
  121. package/src/renderer/components/layout/SortableTab.tsx +1 -0
  122. package/src/renderer/components/layout/TabBarActions.tsx +12 -12
  123. package/src/renderer/components/schedules/SchedulesView.tsx +54 -22
  124. package/src/renderer/components/settings/sections/AdvancedSection.tsx +1 -1
  125. package/src/renderer/components/settings/sections/HarnessSection.tsx +2 -6
  126. package/src/renderer/components/settings/sections/TaskBusSection.tsx +144 -84
  127. package/src/renderer/components/sidebar/SidebarSessions.tsx +23 -0
  128. package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +1 -7
  129. package/src/renderer/components/tasks/TasksView.tsx +343 -0
  130. package/src/renderer/components/team/HarnessSelect.tsx +71 -0
  131. package/src/renderer/components/team/TeamDetailView.tsx +55 -98
  132. package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +21 -12
  133. package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +8 -13
  134. package/src/renderer/components/team/dialogs/LaunchTeamDialog.tsx +1 -1
  135. package/src/renderer/components/team/editor/EditorContextMenu.tsx +8 -23
  136. package/src/renderer/components/team/editor/EditorFileTree.tsx +0 -4
  137. package/src/renderer/components/team/editor/EditorSelectionMenu.tsx +1 -8
  138. package/src/renderer/components/team/editor/ProjectEditorOverlay.tsx +0 -10
  139. package/src/renderer/components/team/kanban/KanbanBoard.tsx +31 -65
  140. package/src/renderer/components/team/members/MemberDetailDialog.tsx +8 -33
  141. package/src/renderer/components/team/messages/MessageComposer.tsx +39 -3
  142. package/src/renderer/components/team/messages/MessagesPanel.tsx +100 -26
  143. package/src/renderer/components/team/messages/StatusBlock.tsx +2 -24
  144. package/src/renderer/components/team/schedule/ScheduleEmptyState.tsx +1 -1
  145. package/src/renderer/components/terminal/TerminalPanel.tsx +156 -0
  146. package/src/renderer/components/ui/MentionableTextarea.tsx +0 -1
  147. package/src/renderer/hooks/useExtensionsTabState.ts +2 -2
  148. package/src/renderer/store/slices/extensionsSlice.ts +42 -107
  149. package/src/renderer/store/slices/scheduleSlice.ts +21 -0
  150. package/src/renderer/store/slices/teamSlice.ts +67 -25
  151. package/src/renderer/types/tabs.ts +1 -0
  152. package/src/shared/types/api.ts +58 -0
  153. package/src/shared/types/extensions/index.ts +1 -0
  154. package/src/shared/types/extensions/mcp.ts +2 -0
  155. package/src/shared/types/extensions/plugin.ts +2 -1
  156. package/src/shared/types/extensions/skill.ts +7 -0
  157. package/src/shared/types/team.ts +104 -1
  158. package/src/shared/utils/providerExtensionCapabilities.ts +1 -1
  159. package/dist-renderer/assets/ProjectEditorOverlay-A4DZTvSy.js +0 -57
  160. package/dist-renderer/assets/channel-Pre42N5O.js +0 -1
  161. package/dist-renderer/assets/classDiagram-2ON5EDUG-CdJsTJsj.js +0 -1
  162. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-CdJsTJsj.js +0 -1
  163. package/dist-renderer/assets/clone-BjQBiNfj.js +0 -1
  164. package/dist-renderer/assets/index-BIOJremZ.css +0 -1
  165. package/src/features/recent-projects/main/adapters/input/http/registerRecentProjectsHttp.ts +0 -30
  166. package/src/features/recent-projects/main/adapters/output/presenters/DashboardRecentProjectsPresenter.ts +0 -27
  167. package/src/features/recent-projects/main/adapters/output/sources/ClaudeRecentProjectsSourceAdapter.ts +0 -91
  168. package/src/features/recent-projects/main/adapters/output/sources/CodexRecentProjectsSourceAdapter.ts +0 -326
  169. package/src/features/recent-projects/main/composition/createRecentProjectsFeature.ts +0 -43
  170. package/src/features/recent-projects/main/index.ts +0 -3
  171. package/src/features/recent-projects/main/infrastructure/cache/InMemoryRecentProjectsCache.ts +0 -34
  172. package/src/features/recent-projects/main/infrastructure/codex/CodexAppServerClient.ts +0 -116
  173. package/src/features/recent-projects/main/infrastructure/identity/RecentProjectIdentityResolver.ts +0 -20
  174. package/src/features/recent-projects/main/infrastructure/identity/normalizeIdentityPath.ts +0 -10
  175. package/src/renderer/components/extensions/apikeys/ApiKeyCard.tsx +0 -143
  176. package/src/renderer/components/extensions/apikeys/ApiKeyFormDialog.tsx +0 -282
  177. package/src/renderer/components/extensions/apikeys/ApiKeysPanel.tsx +0 -280
@@ -208,9 +208,11 @@ export const MessagesPanel = memo(function MessagesPanel({
208
208
  teams,
209
209
  openTeamTab,
210
210
  messages,
211
- messagesState,
211
+ hasMore,
212
+ loadingOlderMessages,
212
213
  loadOlderTeamMessages,
213
214
  refreshTeamMessagesHead,
215
+ addOptimisticTeamMessage,
214
216
  } = useStore(
215
217
  useShallow((s) => ({
216
218
  sendTeamMessage: s.sendTeamMessage,
@@ -223,24 +225,30 @@ export const MessagesPanel = memo(function MessagesPanel({
223
225
  teams: s.teams,
224
226
  openTeamTab: s.openTeamTab,
225
227
  messages: selectTeamMessages(s, teamName),
226
- messagesState: teamName ? s.teamMessagesByName[teamName] : undefined,
228
+ // Subscribe to only the primitive flags the panel renders. The full
229
+ // cache entry object is rebuilt on every (even no-op) head refresh —
230
+ // selecting it wholesale would re-render this heavy panel every poll.
231
+ hasMore: teamName ? (s.teamMessagesByName[teamName]?.hasMore ?? false) : false,
232
+ loadingOlderMessages: teamName
233
+ ? (s.teamMessagesByName[teamName]?.loadingOlder ?? false)
234
+ : false,
227
235
  loadOlderTeamMessages: s.loadOlderTeamMessages,
228
236
  refreshTeamMessagesHead: s.refreshTeamMessagesHead,
237
+ addOptimisticTeamMessage: s.addOptimisticTeamMessage,
229
238
  }))
230
239
  );
231
240
  const bootstrapHeadRefreshAttemptedForTeamRef = useRef<string | null>(null);
232
241
 
233
242
  const loadOlderMessages = useCallback(async () => {
234
- if (!messagesState?.hasMore || messagesState.loadingHead || messagesState.loadingOlder) {
243
+ // Read the live cache entry instead of subscribing to it — loadingHead
244
+ // toggles on every background head refresh and must not re-render us.
245
+ const entry = useStore.getState().teamMessagesByName[teamName];
246
+ if (!entry?.hasMore || entry.loadingHead || entry.loadingOlder) {
235
247
  return;
236
248
  }
237
249
  await loadOlderTeamMessages(teamName);
238
- }, [loadOlderTeamMessages, messagesState, teamName]);
250
+ }, [loadOlderTeamMessages, teamName]);
239
251
 
240
- const messagesLoading =
241
- (messagesState?.loadingHead ?? false) || (messagesState?.loadingOlder ?? false);
242
- const loadingOlderMessages = messagesState?.loadingOlder ?? false;
243
- const hasMore = messagesState?.hasMore ?? false;
244
252
  const effectiveMessages = messages;
245
253
  const loadedMessageCount = effectiveMessages.length;
246
254
  const autoLoadOlderLockRef = useRef(false);
@@ -269,12 +277,11 @@ export const MessagesPanel = memo(function MessagesPanel({
269
277
 
270
278
  const maybeAutoLoadOlderMessages = useCallback(
271
279
  (scrollTop: number) => {
272
- if (
273
- scrollTop > AUTO_LOAD_OLDER_SCROLL_TOP_PX ||
274
- !hasMore ||
275
- messagesState?.loadingHead ||
276
- loadingOlderMessages
277
- ) {
280
+ if (scrollTop > AUTO_LOAD_OLDER_SCROLL_TOP_PX || !hasMore || loadingOlderMessages) {
281
+ return;
282
+ }
283
+ // loadingHead is read live (not subscribed) to avoid per-poll re-renders.
284
+ if (useStore.getState().teamMessagesByName[teamName]?.loadingHead) {
278
285
  return;
279
286
  }
280
287
  if (autoLoadOlderLockRef.current) {
@@ -283,7 +290,7 @@ export const MessagesPanel = memo(function MessagesPanel({
283
290
  autoLoadOlderLockRef.current = true;
284
291
  void loadOlderMessages();
285
292
  },
286
- [hasMore, loadOlderMessages, loadingOlderMessages, messagesState?.loadingHead]
293
+ [hasMore, loadOlderMessages, loadingOlderMessages, teamName]
287
294
  );
288
295
 
289
296
  useEffect(() => {
@@ -377,7 +384,9 @@ export const MessagesPanel = memo(function MessagesPanel({
377
384
  return () => {
378
385
  cancelled = true;
379
386
  };
380
- }, [teamName]);
387
+ // Refetch when the lead session id changes (e.g. a new session is spawned)
388
+ // so the session list/selector reflects the updated id without a remount.
389
+ }, [teamName, currentLeadSessionId]);
381
390
 
382
391
  const selectedSession = useMemo(
383
392
  () => teamSessions.find((session) => session.sessionKey === selectedSessionKey) ?? null,
@@ -453,7 +462,10 @@ export const MessagesPanel = memo(function MessagesPanel({
453
462
  bootstrapHeadRefreshAttemptedForTeamRef.current = null;
454
463
  return;
455
464
  }
456
- if (messagesState?.loadingHead || messagesState?.loadingOlder) {
465
+ // Read loading flags live rather than subscribing — they toggle on every
466
+ // background head refresh and must not drive this bootstrap effect.
467
+ const entry = useStore.getState().teamMessagesByName[teamName];
468
+ if (entry?.loadingHead || entry?.loadingOlder) {
457
469
  return;
458
470
  }
459
471
  if (bootstrapHeadRefreshAttemptedForTeamRef.current === teamName) {
@@ -461,13 +473,7 @@ export const MessagesPanel = memo(function MessagesPanel({
461
473
  }
462
474
  bootstrapHeadRefreshAttemptedForTeamRef.current = teamName;
463
475
  void refreshTeamMessagesHead(teamName).catch(() => undefined);
464
- }, [
465
- effectiveMessages.length,
466
- messagesState?.loadingHead,
467
- messagesState?.loadingOlder,
468
- refreshTeamMessagesHead,
469
- teamName,
470
- ]);
476
+ }, [effectiveMessages.length, refreshTeamMessagesHead, teamName]);
471
477
 
472
478
  useLayoutEffect(() => {
473
479
  if (position !== 'sidebar') return;
@@ -534,7 +540,7 @@ export const MessagesPanel = memo(function MessagesPanel({
534
540
  const sessionScopedMessages = useMemo(() => {
535
541
  const newestFirst = (items: InboxMessage[]) =>
536
542
  [...items].sort((a, b) => Date.parse(b.timestamp) - Date.parse(a.timestamp));
537
- if (!selectedSessionKey) return [];
543
+ if (!selectedSessionKey) return newestFirst(effectiveMessages);
538
544
  if (selectedSession && !selectedIsHermitLocalSession) {
539
545
  if (!selectedSessionDetail) {
540
546
  return [];
@@ -731,12 +737,77 @@ export const MessagesPanel = memo(function MessagesPanel({
731
737
  fromMember: 'user',
732
738
  toTeam,
733
739
  text,
740
+ sessionKey:
741
+ selectedSessionKey && selectedSessionKey !== '__unassigned__'
742
+ ? selectedSessionKey
743
+ : undefined,
734
744
  taskRefs,
735
745
  actionMode,
736
746
  summary,
737
747
  });
738
748
  },
739
- [teamName, sendCrossTeamMessage]
749
+ [teamName, selectedSessionKey, sendCrossTeamMessage]
750
+ );
751
+
752
+ const handleDispatchTaskToTeam = useCallback(
753
+ async (toTeam: string, subject: string, description: string) => {
754
+ const now = Date.now();
755
+ const optimisticMessageId = `optimistic-cross-team-${now}`;
756
+ addOptimisticTeamMessage(teamName, {
757
+ from: 'user',
758
+ to: toTeam,
759
+ text: `@${toTeam} ${subject}`,
760
+ timestamp: new Date(now).toISOString(),
761
+ read: true,
762
+ messageId: optimisticMessageId,
763
+ source: 'cross_team_sent',
764
+ session:
765
+ selectedSessionKey && selectedSessionKey !== '__unassigned__'
766
+ ? { key: selectedSessionKey }
767
+ : undefined,
768
+ });
769
+ try {
770
+ await sendCrossTeamMessage({
771
+ fromTeam: teamName,
772
+ fromMember: 'user',
773
+ toTeam,
774
+ text: description,
775
+ messageId: optimisticMessageId,
776
+ });
777
+ } catch (error) {
778
+ const rawMessage = error instanceof Error ? error.message : '跨团队任务派发失败';
779
+ const readableMessage = rawMessage.includes('Redis not configured')
780
+ ? '无法派发给其他团队:Redis 未配置或未连接。请先在设置里开启团队总线并配置 Redis。'
781
+ : rawMessage.includes('Distributed collaboration is not enabled')
782
+ ? '无法派发给其他团队:团队总线/分布式团队协作未开启。请先在设置里开启。'
783
+ : `无法派发给 ${toTeam}:${rawMessage}`;
784
+ addOptimisticTeamMessage(teamName, {
785
+ from: 'system',
786
+ to: 'user',
787
+ text: readableMessage,
788
+ timestamp: new Date(Date.now()).toISOString(),
789
+ read: true,
790
+ messageId: `optimistic-cross-team-error-${Date.now()}`,
791
+ source: 'system_notification',
792
+ });
793
+ window.dispatchEvent(new CustomEvent('collab:refresh'));
794
+ await refreshTeamMessagesHead(teamName);
795
+ return false;
796
+ }
797
+ window.dispatchEvent(new CustomEvent('collab:refresh'));
798
+ await refreshTeamMessagesHead(teamName);
799
+ window.setTimeout(() => {
800
+ void refreshTeamMessagesHead(teamName);
801
+ }, 300);
802
+ return true;
803
+ },
804
+ [
805
+ addOptimisticTeamMessage,
806
+ teamName,
807
+ refreshTeamMessagesHead,
808
+ selectedSessionKey,
809
+ sendCrossTeamMessage,
810
+ ]
740
811
  );
741
812
 
742
813
  const moveToInline = useCallback(() => {
@@ -869,6 +940,7 @@ export const MessagesPanel = memo(function MessagesPanel({
869
940
  onSessionChange={setSelectedSessionKey}
870
941
  textareaRef={composerTextareaRef}
871
942
  onSend={handleSend}
943
+ onDispatchTask={handleDispatchTaskToTeam}
872
944
  />
873
945
  <StatusBlock
874
946
  members={members}
@@ -1065,6 +1137,7 @@ export const MessagesPanel = memo(function MessagesPanel({
1065
1137
  onSessionChange={setSelectedSessionKey}
1066
1138
  textareaRef={composerTextareaRef}
1067
1139
  onSend={handleSend}
1140
+ onDispatchTask={handleDispatchTaskToTeam}
1068
1141
  />
1069
1142
  <StatusBlock
1070
1143
  members={members}
@@ -1352,6 +1425,7 @@ export const MessagesPanel = memo(function MessagesPanel({
1352
1425
  onSessionChange={setSelectedSessionKey}
1353
1426
  textareaRef={composerTextareaRef}
1354
1427
  onSend={handleSend}
1428
+ onDispatchTask={handleDispatchTaskToTeam}
1355
1429
  />
1356
1430
  </div>
1357
1431
  </div>
@@ -3,7 +3,6 @@ import { useEffect, useMemo, useState } from 'react';
3
3
  import { computePendingCrossTeamReplies } from '@renderer/utils/crossTeamPendingReplies';
4
4
  import { ChevronRight } from 'lucide-react';
5
5
 
6
- import { ActiveTasksBlock } from '../activity/ActiveTasksBlock';
7
6
  import { PendingRepliesBlock } from '../activity/PendingRepliesBlock';
8
7
 
9
8
  import type { InboxMessage, ResolvedTeamMember, TeamTaskWithKanban } from '@shared/types';
@@ -29,13 +28,10 @@ interface StatusBlockProps {
29
28
  */
30
29
  export const StatusBlock = ({
31
30
  members,
32
- tasks,
33
31
  messages,
34
32
  pendingRepliesByMember,
35
- position,
36
33
  layout = 'overlay',
37
34
  onMemberClick,
38
- onTaskClick,
39
35
  }: StatusBlockProps): React.JSX.Element | null => {
40
36
  const [collapsed, setCollapsed] = useState(false);
41
37
  const [nowMs, setNowMs] = useState(() => Date.now());
@@ -50,21 +46,11 @@ export const StatusBlock = ({
50
46
  );
51
47
  return hasMemberPendingReplies || pendingCrossTeamReplies.length > 0;
52
48
  }, [members, pendingRepliesByMember, pendingCrossTeamReplies.length]);
53
- const hasActiveTasks = useMemo(() => {
54
- const tMap = new Map(tasks.map((t) => [t.id, t]));
55
- return members.some((m) => {
56
- if (!m.currentTaskId) return false;
57
- const task = tMap.get(m.currentTaskId);
58
- if (task && (task.reviewState === 'approved' || task.status === 'completed')) return false;
59
- return true;
60
- });
61
- }, [members, tasks]);
62
49
 
63
50
  /** Whether the Status block has any visible items. */
64
51
  const hasItems = useMemo(() => {
65
- if (hasPendingReplies) return true;
66
- return hasActiveTasks;
67
- }, [hasActiveTasks, hasPendingReplies]);
52
+ return hasPendingReplies;
53
+ }, [hasPendingReplies]);
68
54
 
69
55
  // Only run the 1-second timer when the block actually has content to show.
70
56
  useEffect(() => {
@@ -111,14 +97,6 @@ export const StatusBlock = ({
111
97
  onMemberClick={onMemberClick}
112
98
  />
113
99
  ) : null}
114
- <ActiveTasksBlock
115
- members={members}
116
- tasks={tasks}
117
- defaultCollapsed={position === 'sidebar'}
118
- headerRight={!hasPendingReplies ? flowInlineToggle : undefined}
119
- onMemberClick={onMemberClick}
120
- onTaskClick={onTaskClick}
121
- />
122
100
  </div>
123
101
  )}
124
102
  </>
@@ -8,7 +8,7 @@ export const ScheduleEmptyState = (): React.JSX.Element => (
8
8
  <div className="space-y-1">
9
9
  <p className="text-xs font-medium text-[var(--color-text-secondary)]">暂无定时计划</p>
10
10
  <p className="text-[11px] text-[var(--color-text-muted)]">
11
- 创建计划后,可按 cron 时间自动运行团队任务。
11
+ 创建计划后,可按 cron 时间自动运行团队。
12
12
  </p>
13
13
  </div>
14
14
  </div>
@@ -0,0 +1,156 @@
1
+ /**
2
+ * TerminalPanel — a good-looking, read-only terminal-style panel for rendering
3
+ * command / CLI output faithfully.
4
+ *
5
+ * Unlike the structured markdown renderers, this preserves the raw terminal feel:
6
+ * - full ANSI color / decoration fidelity (via `anser`)
7
+ * - monospace, whitespace-exact output
8
+ * - carriage-return (\r) overwrite handling so progress bars settle to their
9
+ * final frame instead of dumping every intermediate line
10
+ * - optional `$ command` prompt line so a Bash call reads like a real terminal
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.
15
+ */
16
+
17
+ import { useMemo, useState } from 'react';
18
+
19
+ import Anser, { type AnserJsonEntry } from 'anser';
20
+ import { Check, Copy } from 'lucide-react';
21
+
22
+ interface TerminalPanelProps {
23
+ /** Raw output text, may contain ANSI escape sequences. */
24
+ text: string;
25
+ /** Optional command to render as a `$ command` prompt line above the output. */
26
+ command?: string;
27
+ /** Optional label shown in the header bar (e.g. a short description). */
28
+ title?: string;
29
+ /** Max body height in px before scrolling. Defaults to 384. */
30
+ maxHeight?: number;
31
+ className?: string;
32
+ }
33
+
34
+ /**
35
+ * Collapse carriage-return overwrites within each line and strip non-color
36
+ * escape sequences (cursor moves, screen clears, OSC) that would otherwise
37
+ * render as garbage. SGR color codes are left intact for `anser`.
38
+ */
39
+ function normalizeTerminalText(raw: string): string {
40
+ // Strip OSC sequences: ESC ] ... BEL or ESC ] ... ESC \
41
+ let out = raw.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, '');
42
+ // Strip CSI sequences that are NOT SGR ("m"): cursor movement, erase, etc.
43
+ out = out.replace(/\x1b\[[0-9;?]*[A-Za-ln-z]/g, '');
44
+ // Collapse \r overwrites per line: later segments overwrite earlier from col 0.
45
+ out = out
46
+ .split('\n')
47
+ .map((line) => {
48
+ if (!line.includes('\r')) return line;
49
+ let acc = '';
50
+ for (const seg of line.split('\r')) {
51
+ acc = seg.length >= acc.length ? seg : seg + acc.slice(seg.length);
52
+ }
53
+ return acc;
54
+ })
55
+ .join('\n');
56
+ return out;
57
+ }
58
+
59
+ function styleForSegment(seg: AnserJsonEntry): React.CSSProperties {
60
+ const style: React.CSSProperties = {};
61
+ if (seg.fg) style.color = `rgb(${seg.fg})`;
62
+ if (seg.bg) style.backgroundColor = `rgb(${seg.bg})`;
63
+ const decorations = seg.decorations ?? [];
64
+ if (decorations.includes('bold')) style.fontWeight = 600;
65
+ if (decorations.includes('italic')) style.fontStyle = 'italic';
66
+ if (decorations.includes('underline')) style.textDecoration = 'underline';
67
+ if (decorations.includes('dim')) style.opacity = 0.6;
68
+ return style;
69
+ }
70
+
71
+ const MONO_FONT =
72
+ 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace';
73
+
74
+ export const TerminalPanel = ({
75
+ text,
76
+ command,
77
+ title,
78
+ maxHeight = 384,
79
+ className,
80
+ }: TerminalPanelProps): React.JSX.Element => {
81
+ const [copied, setCopied] = useState(false);
82
+
83
+ const segments = useMemo<AnserJsonEntry[]>(
84
+ () =>
85
+ Anser.ansiToJson(normalizeTerminalText(text ?? ''), {
86
+ json: true,
87
+ use_classes: false,
88
+ remove_empty: false,
89
+ }),
90
+ [text]
91
+ );
92
+
93
+ const handleCopy = (): void => {
94
+ const payload = command ? `$ ${command}\n${text ?? ''}` : (text ?? '');
95
+ void navigator.clipboard?.writeText(payload).then(() => {
96
+ setCopied(true);
97
+ setTimeout(() => setCopied(false), 1500);
98
+ });
99
+ };
100
+
101
+ return (
102
+ <div
103
+ className={`overflow-hidden rounded-lg border ${className ?? ''}`}
104
+ style={{ borderColor: 'rgba(255,255,255,0.08)', backgroundColor: '#0c0c0f' }}
105
+ >
106
+ {/* Header / window chrome */}
107
+ <div
108
+ className="flex items-center gap-2 px-3 py-1.5"
109
+ style={{
110
+ backgroundColor: '#16161b',
111
+ borderBottom: '1px solid rgba(255,255,255,0.06)',
112
+ }}
113
+ >
114
+ <span className="flex items-center gap-1.5">
115
+ <span className="size-2.5 rounded-full" style={{ backgroundColor: '#ff5f57' }} />
116
+ <span className="size-2.5 rounded-full" style={{ backgroundColor: '#febc2e' }} />
117
+ <span className="size-2.5 rounded-full" style={{ backgroundColor: '#28c840' }} />
118
+ </span>
119
+ <span
120
+ className="ml-1 flex-1 truncate text-[11px]"
121
+ style={{ color: 'rgba(255,255,255,0.45)', fontFamily: MONO_FONT }}
122
+ >
123
+ {title ?? (command ? command : 'terminal')}
124
+ </span>
125
+ <button
126
+ type="button"
127
+ onClick={handleCopy}
128
+ className="flex items-center gap-1 rounded px-1.5 py-0.5 text-[10px] transition-colors"
129
+ style={{ color: 'rgba(255,255,255,0.45)' }}
130
+ title="复制"
131
+ >
132
+ {copied ? <Check className="size-3" /> : <Copy className="size-3" />}
133
+ {copied ? '已复制' : '复制'}
134
+ </button>
135
+ </div>
136
+
137
+ {/* Body */}
138
+ <pre
139
+ className="overflow-auto whitespace-pre-wrap break-all px-3 py-2.5 text-xs leading-relaxed"
140
+ style={{ maxHeight, fontFamily: MONO_FONT, color: '#d4d4d4', margin: 0 }}
141
+ >
142
+ {command && (
143
+ <div className="mb-1">
144
+ <span style={{ color: '#28c840' }}>$ </span>
145
+ <span style={{ color: '#e8e8e8' }}>{command}</span>
146
+ </div>
147
+ )}
148
+ {segments.map((seg, i) => (
149
+ <span key={i} style={styleForSegment(seg)}>
150
+ {seg.content}
151
+ </span>
152
+ ))}
153
+ </pre>
154
+ </div>
155
+ );
156
+ };
@@ -1069,7 +1069,6 @@ export const MentionableTextarea = React.forwardRef<HTMLTextAreaElement, Mention
1069
1069
  const rotatingTips = React.useMemo(
1070
1070
  () => [
1071
1071
  'Tips:输入 @ 可提及成员、团队或文件,输入 # 可引用任务。',
1072
- 'Tips:输入“创建任务”可以把事项加入任务看板。',
1073
1072
  'Tips:不要把所有工作都堆给团队负责人,可以让负责人分配给合适的成员。',
1074
1073
  ...extraTips,
1075
1074
  ],
@@ -16,7 +16,7 @@ import type {
16
16
  PluginSortField,
17
17
  } from '@shared/types/extensions';
18
18
 
19
- export type ExtensionsSubTab = 'plugins' | 'mcp-servers' | 'skills' | 'api-keys';
19
+ export type ExtensionsSubTab = 'plugins' | 'mcp-servers' | 'skills' | 'env-vars';
20
20
  export type SkillsSortState = 'name-asc' | 'recent-desc';
21
21
 
22
22
  interface PluginSortState {
@@ -33,7 +33,7 @@ const DEFAULT_FILTERS: PluginFilters = {
33
33
 
34
34
  export function useExtensionsTabState() {
35
35
  // ── Sub-tab navigation ──
36
- const [activeSubTab, setActiveSubTab] = useState<ExtensionsSubTab>('mcp-servers');
36
+ const [activeSubTab, setActiveSubTab] = useState<ExtensionsSubTab>('plugins');
37
37
 
38
38
  // ── Plugin filters & sort ──
39
39
  const [pluginFilters, setPluginFilters] = useState<PluginFilters>(DEFAULT_FILTERS);