@yancyyu/openhermit 1.6.37 → 1.6.39

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 (205) hide show
  1. package/dist-renderer/assets/ProjectEditorOverlay-krO5vQxX.js +58 -0
  2. package/dist-renderer/assets/{TeamGraphOverlay-DYT3bwFR.js → TeamGraphOverlay-DqhQzcTr.js} +1 -1
  3. package/dist-renderer/assets/{_basePickBy-Dbt_EU-e.js → _basePickBy-B7kSYPxr.js} +1 -1
  4. package/dist-renderer/assets/{_baseUniq-DWo68sXI.js → _baseUniq-CnjxqwAk.js} +1 -1
  5. package/dist-renderer/assets/{arc-DXH1iZQK.js → arc-CLeZuINP.js} +1 -1
  6. package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-cjffS2Qr.js → architectureDiagram-VXUJARFQ-QKtqaqdY.js} +1 -1
  7. package/dist-renderer/assets/{blockDiagram-VD42YOAC-BKdZF02Y.js → blockDiagram-VD42YOAC-BqdrzO_f.js} +1 -1
  8. package/dist-renderer/assets/{c4Diagram-YG6GDRKO-CN27pqaI.js → c4Diagram-YG6GDRKO-gwPlCxDC.js} +1 -1
  9. package/dist-renderer/assets/channel-DpMHF50r.js +1 -0
  10. package/dist-renderer/assets/{chunk-4BX2VUAB-CXPCI7g_.js → chunk-4BX2VUAB-C6XLurL4.js} +1 -1
  11. package/dist-renderer/assets/{chunk-55IACEB6-BGAXQZRC.js → chunk-55IACEB6-Ds6quhEP.js} +1 -1
  12. package/dist-renderer/assets/{chunk-B4BG7PRW-TPDaA_KQ.js → chunk-B4BG7PRW-5UlA1_e9.js} +1 -1
  13. package/dist-renderer/assets/{chunk-DI55MBZ5-D1ADe_tq.js → chunk-DI55MBZ5-ywFrqIsY.js} +1 -1
  14. package/dist-renderer/assets/{chunk-FMBD7UC4-Beimtg3a.js → chunk-FMBD7UC4-C7ifUA17.js} +1 -1
  15. package/dist-renderer/assets/{chunk-QN33PNHL-OjNBu854.js → chunk-QN33PNHL-BxGCo80U.js} +1 -1
  16. package/dist-renderer/assets/{chunk-QZHKN3VN-DinqvbH8.js → chunk-QZHKN3VN-B2CuaZs6.js} +1 -1
  17. package/dist-renderer/assets/{chunk-TZMSLE5B-BfFtlPSZ.js → chunk-TZMSLE5B-Ds1hInvp.js} +1 -1
  18. package/dist-renderer/assets/classDiagram-2ON5EDUG-CBYCBVRl.js +1 -0
  19. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-CBYCBVRl.js +1 -0
  20. package/dist-renderer/assets/clone-DcMF6Psb.js +1 -0
  21. package/dist-renderer/assets/{cose-bilkent-S5V4N54A-D9z9Dgt7.js → cose-bilkent-S5V4N54A-Cz1GVtLp.js} +1 -1
  22. package/dist-renderer/assets/{dagre-6UL2VRFP-n1g-DhEE.js → dagre-6UL2VRFP-BrmR-P4h.js} +1 -1
  23. package/dist-renderer/assets/{diagram-PSM6KHXK-BvxFq-BE.js → diagram-PSM6KHXK-DbNjC5Rg.js} +1 -1
  24. package/dist-renderer/assets/{diagram-QEK2KX5R-wVnJuwza.js → diagram-QEK2KX5R-qkRX5_Mq.js} +1 -1
  25. package/dist-renderer/assets/{diagram-S2PKOQOG-B707WJQw.js → diagram-S2PKOQOG-CyL5rCv2.js} +1 -1
  26. package/dist-renderer/assets/{erDiagram-Q2GNP2WA-C-_1dGHs.js → erDiagram-Q2GNP2WA-Dox3-bA5.js} +1 -1
  27. package/dist-renderer/assets/{flowDiagram-NV44I4VS-CMTSi3H6.js → flowDiagram-NV44I4VS-BtkaxlDL.js} +1 -1
  28. package/dist-renderer/assets/{ganttDiagram-JELNMOA3-DZ0bNrAA.js → ganttDiagram-JELNMOA3-Dhy_d9GK.js} +1 -1
  29. package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-DNVfGooQ.js → gitGraphDiagram-V2S2FVAM-B5XRhIQA.js} +1 -1
  30. package/dist-renderer/assets/{graph-865j_tM_.js → graph-CsoEwUhS.js} +1 -1
  31. package/dist-renderer/assets/{index-C_F9N5x-.js → index-BWPWmJNo.js} +1 -1
  32. package/dist-renderer/assets/{index-LwDIsXJN.js → index-Bu2R-Se7.js} +586 -740
  33. package/dist-renderer/assets/index-CnWV3BhG.css +32 -0
  34. package/dist-renderer/assets/{index-DuUaf8at.js → index-D-3KgskL.js} +1 -1
  35. package/dist-renderer/assets/{index-BTx1nc4T.js → index-DGEBzLNT.js} +1 -1
  36. package/dist-renderer/assets/{index-2EW-eu3q.js → index-NhHNs2Oo.js} +1 -1
  37. package/dist-renderer/assets/{index-4dEMStJj.js → index-h17WuEyf.js} +1 -1
  38. package/dist-renderer/assets/{infoDiagram-HS3SLOUP-CyqtElLq.js → infoDiagram-HS3SLOUP-hMGmNojH.js} +1 -1
  39. package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-BvjQ0Hm0.js → journeyDiagram-XKPGCS4Q-DXV2rBDl.js} +1 -1
  40. package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-CJJ-k0zT.js → kanban-definition-3W4ZIXB7-Bf99WLRy.js} +1 -1
  41. package/dist-renderer/assets/{layout-CnV6rQAG.js → layout-C3XWrpwo.js} +1 -1
  42. package/dist-renderer/assets/{linear-Cw3UQgyX.js → linear-OEEcn8KN.js} +1 -1
  43. package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-C5tDaGSK.js → mindmap-definition-VGOIOE7T-Dpi3S2x4.js} +1 -1
  44. package/dist-renderer/assets/{pieDiagram-ADFJNKIX-CiIpPsau.js → pieDiagram-ADFJNKIX-xTPPhtNx.js} +1 -1
  45. package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-C3gtowNj.js → quadrantDiagram-AYHSOK5B-euniyDlz.js} +1 -1
  46. package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-CXBTrAnU.js → requirementDiagram-UZGBJVZJ-D9Uiw4kF.js} +1 -1
  47. package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-wziX77xG.js → sankeyDiagram-TZEHDZUN-CySU4nED.js} +1 -1
  48. package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-sYqopcrj.js → sequenceDiagram-WL72ISMW-JVGpET6V.js} +1 -1
  49. package/dist-renderer/assets/splashScene-D0YB9uxm.js +17 -0
  50. package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-Bl1-0_Cp.js → stateDiagram-FKZM4ZOC-B2FY5qqi.js} +1 -1
  51. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-DcoMiR8H.js +1 -0
  52. package/dist-renderer/assets/{timeline-definition-IT6M3QCI-CIRjJUBo.js → timeline-definition-IT6M3QCI-DmycNUUe.js} +1 -1
  53. package/dist-renderer/assets/{treemap-GDKQZRPO-CVPuNe1n.js → treemap-GDKQZRPO-DPq4gZuB.js} +1 -1
  54. package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-3nT9yHwp.js → xychartDiagram-PRI3JC2R-J6VVJzRq.js} +1 -1
  55. package/dist-renderer/index.html +20 -53
  56. package/package.json +25 -18
  57. package/src/main/ipc/extensions.ts +30 -50
  58. package/src/main/server.ts +890 -247
  59. package/src/main/services/extensions/ExtensionFacadeService.ts +4 -56
  60. package/src/main/services/extensions/catalog/PluginCatalogService.ts +4 -2
  61. package/src/main/services/extensions/library/McpLibraryService.ts +243 -0
  62. package/src/main/services/session-intelligence/ConversationTelemetryService.ts +1101 -0
  63. package/src/main/services/session-intelligence/LocalSessionScanner.ts +512 -0
  64. package/src/main/services/session-intelligence/SessionUsageParser.ts +4 -4
  65. package/src/main/services/session-intelligence/UsageTelemetryService.ts +14 -1
  66. package/src/main/services/system-manager/SystemManagerConfigService.ts +122 -0
  67. package/src/main/services/system-manager/SystemManagerPtyService.ts +233 -0
  68. package/src/main/services/system-manager/WorkflowPromptService.ts +75 -0
  69. package/src/main/services/teams-mvp/TaskDispatchService.ts +32 -8
  70. package/src/main/services/teams-mvp/TeamProvisioningService.ts +39 -2
  71. package/src/main/services/teams-mvp/TeamWorkspaceService.ts +22 -4
  72. package/src/main/utils/teamProjectResolution.ts +15 -0
  73. package/src/renderer/App.tsx +8 -4
  74. package/src/renderer/api/httpClient.ts +174 -38
  75. package/src/renderer/api/providers.ts +23 -2
  76. package/src/renderer/assets/participant-avatars/01.svg +3 -0
  77. package/src/renderer/assets/participant-avatars/02.svg +3 -0
  78. package/src/renderer/assets/participant-avatars/03.svg +3 -0
  79. package/src/renderer/assets/participant-avatars/04.svg +3 -0
  80. package/src/renderer/assets/participant-avatars/05.svg +3 -0
  81. package/src/renderer/assets/participant-avatars/06.svg +3 -0
  82. package/src/renderer/assets/participant-avatars/07.svg +3 -0
  83. package/src/renderer/assets/participant-avatars/08.svg +3 -0
  84. package/src/renderer/assets/participant-avatars/09.svg +3 -0
  85. package/src/renderer/assets/participant-avatars/10.svg +3 -0
  86. package/src/renderer/assets/participant-avatars/11.svg +3 -0
  87. package/src/renderer/assets/participant-avatars/12.svg +3 -0
  88. package/src/renderer/assets/participant-avatars/13.svg +3 -0
  89. package/src/renderer/components/common/TerminalPane.tsx +213 -0
  90. package/src/renderer/components/dashboard/DashboardView.tsx +9 -36
  91. package/src/renderer/components/extensions/ExtensionStoreView.tsx +12 -221
  92. package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +1 -1
  93. package/src/renderer/components/extensions/mcp/McpLibraryEnableDialog.tsx +305 -0
  94. package/src/renderer/components/extensions/mcp/McpLibraryEntryDialog.tsx +418 -0
  95. package/src/renderer/components/extensions/mcp/McpLibraryPanel.tsx +404 -0
  96. package/src/renderer/components/extensions/plugins/PluginCard.tsx +10 -2
  97. package/src/renderer/components/extensions/plugins/PluginsPanel.tsx +40 -22
  98. package/src/renderer/components/extensions/skills/SkillsLibraryPanel.tsx +335 -0
  99. package/src/renderer/components/layout/PaneContent.tsx +8 -1
  100. package/src/renderer/components/layout/Sidebar.tsx +11 -54
  101. package/src/renderer/components/layout/SortableTab.tsx +20 -31
  102. package/src/renderer/components/layout/TabBar.tsx +1 -1
  103. package/src/renderer/components/layout/TabContextMenu.tsx +1 -1
  104. package/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx +768 -157
  105. package/src/renderer/components/schedules/SchedulesView.tsx +51 -462
  106. package/src/renderer/components/schedules/calendar/CalendarDayView.tsx +173 -0
  107. package/src/renderer/components/schedules/calendar/CalendarEventBlock.tsx +113 -0
  108. package/src/renderer/components/schedules/calendar/CalendarHeader.tsx +148 -0
  109. package/src/renderer/components/schedules/calendar/CalendarMonthView.tsx +142 -0
  110. package/src/renderer/components/schedules/calendar/CalendarWeekView.tsx +219 -0
  111. package/src/renderer/components/schedules/calendar/ScheduleCalendarBoard.tsx +41 -0
  112. package/src/renderer/components/schedules/calendar/TeamGanttView.tsx +405 -0
  113. package/src/renderer/components/schedules/calendar/computeOccurrences.ts +234 -0
  114. package/src/renderer/components/schedules/calendar/index.ts +2 -0
  115. package/src/renderer/components/schedules/calendar/types.ts +44 -0
  116. package/src/renderer/components/settings/SettingsTabs.tsx +50 -55
  117. package/src/renderer/components/settings/SettingsView.tsx +30 -35
  118. package/src/renderer/components/settings/components/SettingsSectionHeader.tsx +5 -1
  119. package/src/renderer/components/settings/components/SettingsSelect.tsx +5 -3
  120. package/src/renderer/components/settings/components/SettingsToggle.tsx +2 -2
  121. package/src/renderer/components/settings/sections/AdvancedSection.tsx +11 -42
  122. package/src/renderer/components/settings/sections/CliStatusSection.tsx +71 -112
  123. package/src/renderer/components/settings/sections/ConfigEditorDialog.tsx +1 -1
  124. package/src/renderer/components/settings/sections/GeneralSection.tsx +11 -3
  125. package/src/renderer/components/settings/sections/HarnessSection.tsx +18 -14
  126. package/src/renderer/components/settings/sections/PlatformsSection.tsx +3 -3
  127. package/src/renderer/components/settings/sections/TaskBusSection.tsx +33 -40
  128. package/src/renderer/components/settings/sections/index.ts +0 -1
  129. package/src/renderer/components/sidebar/SidebarSessions.tsx +182 -4
  130. package/src/renderer/components/sidebar/SidebarTaskItem.tsx +4 -4
  131. package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +39 -4
  132. package/src/renderer/components/splash/splashScene.ts +121 -929
  133. package/src/renderer/components/system-manager/FolderBrowser.tsx +163 -0
  134. package/src/renderer/components/system-manager/SystemManagerView.tsx +351 -0
  135. package/src/renderer/components/tasks/TasksView.tsx +112 -134
  136. package/src/renderer/components/team/CcSessionsSection.tsx +431 -89
  137. package/src/renderer/components/team/CollapsibleTeamSection.tsx +17 -32
  138. package/src/renderer/components/team/TeamDetailView.tsx +325 -114
  139. package/src/renderer/components/team/TeamListView.tsx +108 -123
  140. package/src/renderer/components/team/dialogs/CreateTaskDialog.tsx +2 -2
  141. package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +84 -306
  142. package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +259 -342
  143. package/src/renderer/components/team/dialogs/GlobalTaskDetailDialog.tsx +1 -1
  144. package/src/renderer/components/team/dialogs/LaunchTeamDialog.tsx +17 -15
  145. package/src/renderer/components/team/dialogs/PlatformBindingDialog.tsx +221 -0
  146. package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +7 -0
  147. package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +1 -1
  148. package/src/renderer/components/team/dialogs/RuntimeConfigDialog.tsx +361 -0
  149. package/src/renderer/components/team/dialogs/platformMeta.ts +122 -11
  150. package/src/renderer/components/team/dialogs/useTeamEditForm.ts +17 -5
  151. package/src/renderer/components/team/kanban/KanbanBoard.tsx +9 -9
  152. package/src/renderer/components/team/members/MemberCard.tsx +14 -47
  153. package/src/renderer/components/team/members/MemberDetailDialog.tsx +3 -95
  154. package/src/renderer/components/team/members/MemberDetailStats.tsx +50 -65
  155. package/src/renderer/components/team/messages/MessageComposer.tsx +8 -110
  156. package/src/renderer/components/team/messages/MessagesPanel.tsx +131 -114
  157. package/src/renderer/components/team/schedule/ScheduleStatusBadge.tsx +2 -2
  158. package/src/renderer/components/team/tools/AddMcpInline.tsx +57 -0
  159. package/src/renderer/components/team/tools/AddSkillInline.tsx +61 -0
  160. package/src/renderer/components/team/tools/McpChip.tsx +45 -0
  161. package/src/renderer/components/team/tools/SkillChip.tsx +35 -0
  162. package/src/renderer/components/team/tools/ToolsSection.tsx +556 -0
  163. package/src/renderer/hooks/useExtensionsTabState.ts +3 -114
  164. package/src/renderer/index.css +39 -22
  165. package/src/renderer/index.html +17 -50
  166. package/src/renderer/store/index.ts +2 -1
  167. package/src/renderer/store/slices/scheduleSlice.ts +1 -1
  168. package/src/renderer/store/slices/teamSlice.ts +45 -168
  169. package/src/renderer/utils/claudeCodeOnlyProviders.ts +3 -10
  170. package/src/renderer/utils/memberHelpers.ts +5 -17
  171. package/src/renderer/utils/openCodeRuntimeDeliveryDiagnostics.ts +4 -2
  172. package/src/renderer/utils/providerSlashCommands.ts +0 -5
  173. package/src/renderer/utils/scheduleFormatters.ts +3 -1
  174. package/src/renderer/utils/teamMessageFiltering.ts +14 -1
  175. package/src/renderer/utils/teamModelAvailability.ts +18 -2
  176. package/src/shared/types/api.ts +121 -2
  177. package/src/shared/types/ccConnect.ts +2 -0
  178. package/src/shared/types/extensions/api.ts +9 -0
  179. package/src/shared/types/extensions/index.ts +4 -0
  180. package/src/shared/types/extensions/mcp.ts +41 -0
  181. package/src/shared/types/index.ts +3 -0
  182. package/src/shared/types/systemManager.ts +49 -0
  183. package/src/shared/types/team.ts +29 -0
  184. package/src/shared/types/terminal.ts +4 -2
  185. package/src/shared/utils/extensionNormalizers.ts +29 -0
  186. package/src/shared/utils/providerExtensionCapabilities.ts +2 -2
  187. package/dist-renderer/assets/ProjectEditorOverlay-Va_Vz-zz.js +0 -52
  188. package/dist-renderer/assets/channel-5dJIx68e.js +0 -1
  189. package/dist-renderer/assets/classDiagram-2ON5EDUG-BMGXWJ2d.js +0 -1
  190. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-BMGXWJ2d.js +0 -1
  191. package/dist-renderer/assets/clone-D7FWfGY9.js +0 -1
  192. package/dist-renderer/assets/index-B2z_IyRH.css +0 -1
  193. package/dist-renderer/assets/splashScene-C8lWNnm4.js +0 -1
  194. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-DOYYvDbi.js +0 -1
  195. package/src/main/services/extensions/catalog/GlamaMcpEnrichmentService.ts +0 -190
  196. package/src/main/services/extensions/catalog/McpCatalogAggregator.ts +0 -150
  197. package/src/main/services/extensions/catalog/OfficialMcpRegistryService.ts +0 -381
  198. package/src/main/services/extensions/install/McpInstallService.ts +0 -407
  199. package/src/main/services/extensions/state/McpInstallationStateService.ts +0 -42
  200. package/src/renderer/components/extensions/mcp/McpServerCard.tsx +0 -314
  201. package/src/renderer/components/extensions/mcp/McpServerDetailDialog.tsx +0 -765
  202. package/src/renderer/components/extensions/mcp/McpServersPanel.tsx +0 -593
  203. package/src/renderer/components/extensions/skills/SkillDetailDialog.tsx +0 -372
  204. package/src/renderer/components/extensions/skills/SkillImportDialog.tsx +0 -343
  205. package/src/renderer/components/extensions/skills/SkillsPanel.tsx +0 -659
@@ -60,9 +60,11 @@ import { deriveTaskDisplayId, formatTaskDisplayLabel } from '@shared/utils/taskI
60
60
  import {
61
61
  AlertTriangle,
62
62
  Columns3,
63
+ Download,
63
64
  FolderOpen,
64
65
  GitBranch,
65
66
  History,
67
+ Link,
66
68
  Pencil,
67
69
  Play,
68
70
  Plus,
@@ -71,7 +73,6 @@ import {
71
73
  Loader2,
72
74
  MessageSquare,
73
75
  MoreHorizontal,
74
- Shield,
75
76
  Users,
76
77
  } from 'lucide-react';
77
78
  import { useShallow } from 'zustand/react/shallow';
@@ -79,7 +80,9 @@ import { useShallow } from 'zustand/react/shallow';
79
80
  import { CreateTaskDialog } from './dialogs/CreateTaskDialog';
80
81
  import { EditTeamDialog } from './dialogs/EditTeamDialog';
81
82
  import { LaunchTeamDialog, type TeamLaunchDialogMode } from './dialogs/LaunchTeamDialog';
83
+ import { PlatformBindingDialog } from './dialogs/PlatformBindingDialog';
82
84
  import { ReviewDialog } from './dialogs/ReviewDialog';
85
+ import { RuntimeConfigDialog } from './dialogs/RuntimeConfigDialog';
83
86
  import { SendMessageDialog } from './dialogs/SendMessageDialog';
84
87
  import { TaskDetailDialog } from './dialogs/TaskDetailDialog';
85
88
  import { executeTeamRelaunch } from './dialogs/teamRelaunchFlow';
@@ -88,18 +91,20 @@ import { UNASSIGNED_OWNER } from './kanban/KanbanFilterPopover';
88
91
  import { KanbanSearchInput } from './kanban/KanbanSearchInput';
89
92
  import { TrashDialog } from './kanban/TrashDialog';
90
93
  import { MemberDetailDialog } from './members/MemberDetailDialog';
91
- import { type MemberActivityFilter, type MemberDetailTab } from './members/memberDetailTypes';
94
+
92
95
 
93
96
  import type { TeamMessagesPanelMode } from '@renderer/types/teamMessagesPanelMode';
94
97
  import type { ComponentProps } from 'react';
95
98
 
99
+ import { TerminalPane, type TerminalPaneRef } from '@renderer/components/common/TerminalPane';
100
+
96
101
  const ProjectEditorOverlay = lazy(() =>
97
102
  import('./editor/ProjectEditorOverlay').then((m) => ({ default: m.ProjectEditorOverlay }))
98
103
  );
99
104
  import { MemberList } from './members/MemberList';
100
105
  import { MessagesPanel } from './messages/MessagesPanel';
106
+ import { CcSessionsSection, buildAllSessionsCsv, buildAllSessionsCsvFilename, downloadTextFile, hasDataRows, isExportPayload } from './CcSessionsSection';
101
107
  import { ChangeReviewDialog } from './review/ChangeReviewDialog';
102
- import { ProjectEnvPanel } from '../extensions/env/ProjectEnvPanel';
103
108
  import {
104
109
  getTeamPendingRepliesState,
105
110
  setTeamPendingRepliesState,
@@ -119,6 +124,7 @@ import type { ContextInjection } from '@renderer/types/contextInjection';
119
124
  import type { Session } from '@renderer/types/data';
120
125
  import type { InlineChip } from '@renderer/types/inlineChip';
121
126
  import type {
127
+ CcSession,
122
128
  EffortLevel,
123
129
  GlobalProvider,
124
130
  MemberSpawnStatusEntry,
@@ -132,6 +138,7 @@ import type {
132
138
  TeamTaskWithKanban,
133
139
  TeamViewSnapshot,
134
140
  } from '@shared/types';
141
+ import { SYSTEM_MANAGER_TEAM_NAME } from '@shared/types/team';
135
142
  import type { EditorSelectionAction } from '@shared/types/editor';
136
143
  import type { ContextUsageLike } from '@shared/utils/contextMetrics';
137
144
 
@@ -232,7 +239,7 @@ const TeamOfflineStatusBanner = memo(function TeamOfflineStatusBanner({
232
239
  teamName: string;
233
240
  onLaunch: () => void;
234
241
  }): React.JSX.Element {
235
- const message = '团队离线中';
242
+ const message = '数字员工未运行:当前没有本地 Claude/Agent 进程在运行,已有数据和任务仍会保留。';
236
243
 
237
244
  return (
238
245
  <div
@@ -254,7 +261,7 @@ const TeamOfflineStatusBanner = memo(function TeamOfflineStatusBanner({
254
261
  onClick={onLaunch}
255
262
  >
256
263
  <Play size={12} />
257
- 启动
264
+ 启动数字员工
258
265
  </Button>
259
266
  </div>
260
267
  );
@@ -859,6 +866,43 @@ const TeamMemberDetailDialogBridge = memo(function TeamMemberDetailDialogBridge(
859
866
  );
860
867
  });
861
868
 
869
+ /** Map agent harness type to CLI command name */
870
+ function getCommandForHarness(harness?: string): string {
871
+ switch (harness) {
872
+ case 'claudecode':
873
+ return 'claude';
874
+ case 'codex':
875
+ return 'codex';
876
+ case 'opencode':
877
+ return 'opencode';
878
+ case 'cursor':
879
+ return 'cursor';
880
+ case 'gemini':
881
+ return 'gemini';
882
+ case 'iflow':
883
+ return 'iflow';
884
+ case 'kimi':
885
+ return 'kimi';
886
+ case 'qoder':
887
+ return 'qoder';
888
+ case 'pi':
889
+ return 'pi';
890
+ case 'acp':
891
+ return 'acp';
892
+ case 'tmux':
893
+ return 'tmux';
894
+ case 'devin':
895
+ return 'devin';
896
+ default:
897
+ return 'claude';
898
+ }
899
+ }
900
+
901
+ /** All CcAgentType values run as local CLI agents */
902
+ function isLocalHarness(harness?: string): boolean {
903
+ return !!harness;
904
+ }
905
+
862
906
  export const TeamDetailView = ({
863
907
  teamName,
864
908
  isPaneFocused = false,
@@ -867,10 +911,6 @@ export const TeamDetailView = ({
867
911
  const [requestChangesTaskId, setRequestChangesTaskId] = useState<string | null>(null);
868
912
  const [selectedTask, setSelectedTask] = useState<TeamTaskWithKanban | null>(null);
869
913
  const [selectedMember, setSelectedMember] = useState<ResolvedTeamMember | null>(null);
870
- const [selectedMemberView, setSelectedMemberView] = useState<{
871
- initialTab?: MemberDetailTab;
872
- initialActivityFilter?: MemberActivityFilter;
873
- } | null>(null);
874
914
  const [pendingRepliesByMember, setPendingRepliesByMember] = useState<Record<string, number>>(() =>
875
915
  getTeamPendingRepliesState(teamName)
876
916
  );
@@ -884,7 +924,7 @@ export const TeamDetailView = ({
884
924
  const [removeMemberConfirm, setRemoveMemberConfirm] = useState<string | null>(null);
885
925
  const [updatingRoleLoading, setUpdatingRoleLoading] = useState(false);
886
926
  const [editDialogOpen, setEditDialogOpen] = useState(false);
887
- const [envDialogOpen, setEnvDialogOpen] = useState(false);
927
+ const [bindingDialogOpen, setBindingDialogOpen] = useState(false);
888
928
  const [headerMenuOpen, setHeaderMenuOpen] = useState(false);
889
929
  const [launchDialogState, setLaunchDialogState] = useState<{
890
930
  open: boolean;
@@ -894,8 +934,11 @@ export const TeamDetailView = ({
894
934
  mode: 'launch',
895
935
  });
896
936
  const [editorOpen, setEditorOpen] = useState(false);
937
+ const [terminalHeight, setTerminalHeight] = useState(400);
897
938
  const contentRef = useRef<HTMLDivElement>(null);
898
939
  const provisioningBannerRef = useRef<HTMLDivElement>(null);
940
+ const terminalPaneRef = useRef<TerminalPaneRef>(null);
941
+ const terminalSpawnedForTeamRef = useRef<string | null>(null);
899
942
  const wasProvisioningRef = useRef(false);
900
943
 
901
944
  // Set inert on background content when editor overlay is open (a11y focus trap)
@@ -929,17 +972,11 @@ export const TeamDetailView = ({
929
972
  const {
930
973
  teamName: tn,
931
974
  memberName,
932
- initialTab,
933
- initialActivityFilter,
934
975
  } = (e as CustomEvent).detail ?? {};
935
976
  if (tn !== teamName || !data) return;
936
977
  const member = members.find((m: { name: string }) => m.name === memberName);
937
978
  if (member) {
938
979
  setSelectedMember(member);
939
- setSelectedMemberView({
940
- initialTab,
941
- initialActivityFilter,
942
- });
943
980
  }
944
981
  };
945
982
  const onCreateTask = (e: Event) => {
@@ -1086,6 +1123,56 @@ export const TeamDetailView = ({
1086
1123
  const [sessions, setSessions] = useState<Session[]>([]);
1087
1124
  const [sessionsLoading, setSessionsLoading] = useState(false);
1088
1125
  const [sessionsError, setSessionsError] = useState<string | null>(null);
1126
+ const [ccSessions, setCcSessions] = useState<CcSession[]>([]);
1127
+ const [ccSessionsLoading, setCcSessionsLoading] = useState(false);
1128
+ const [ccSessionsError, setCcSessionsError] = useState<string | null>(null);
1129
+ const [ccExporting, setCcExporting] = useState(false);
1130
+
1131
+ const handleExportAllCcSessions = useCallback(async () => {
1132
+ if (ccExporting) return;
1133
+ setCcExporting(true);
1134
+ try {
1135
+ const params = new URLSearchParams({
1136
+ teamName,
1137
+ format: 'csv',
1138
+ includeContent: 'full',
1139
+ includeToolResults: 'false',
1140
+ includeSystemMessages: 'false',
1141
+ });
1142
+ const res = await fetch(`/api/telemetry/conversations/export?${params.toString()}`);
1143
+ if (res.ok) {
1144
+ const payload = (await res.json()) as unknown;
1145
+ if (isExportPayload(payload) && hasDataRows(payload.content)) {
1146
+ downloadTextFile(payload.content, payload.filename, payload.mimeType);
1147
+ return;
1148
+ }
1149
+ }
1150
+
1151
+ const details = await Promise.all(
1152
+ ccSessions.map(async (session) => {
1153
+ try {
1154
+ const detail = await api.teams.getSessionDetail(
1155
+ teamName,
1156
+ session.id,
1157
+ Math.max(session.historyCount, 1)
1158
+ );
1159
+ return { session, detail };
1160
+ } catch {
1161
+ return { session, detail: null };
1162
+ }
1163
+ })
1164
+ );
1165
+ downloadTextFile(
1166
+ buildAllSessionsCsv(details),
1167
+ buildAllSessionsCsvFilename(teamName),
1168
+ 'text/csv;charset=utf-8'
1169
+ );
1170
+ } catch (err) {
1171
+ console.error('Failed to export all conversation telemetry:', err);
1172
+ } finally {
1173
+ setCcExporting(false);
1174
+ }
1175
+ }, [ccExporting, ccSessions, teamName]);
1089
1176
  const [kanbanFilter, setKanbanFilter] = useState<KanbanFilterState>({
1090
1177
  sessionId: null,
1091
1178
  selectedOwners: new Set(),
@@ -1244,6 +1331,24 @@ export const TeamDetailView = ({
1244
1331
  void fetchDeletedTasks(teamName);
1245
1332
  }, [teamName, selectTeam, fetchDeletedTasks]);
1246
1333
 
1334
+ // Spawn terminal PTY once per team — imperative, no autoSpawn.
1335
+ // TerminalPane lives outside renderBody() so it never unmounts on data refresh.
1336
+ // terminalSpawnedForTeamRef guards against re-spawning on data changes.
1337
+ useEffect(() => {
1338
+ if (!data || !teamName || teamName === SYSTEM_MANAGER_TEAM_NAME) return;
1339
+ const cwd = data.config.projectPath || data.config.executionTarget?.cwd || data.workDir || '';
1340
+ if (!cwd) return;
1341
+ const harness = data.config.agentType || data.harness;
1342
+ const spawnKey = `${teamName}:${harness ?? ''}`;
1343
+ if (terminalSpawnedForTeamRef.current === spawnKey) return;
1344
+ terminalSpawnedForTeamRef.current = spawnKey;
1345
+ void terminalPaneRef.current?.spawn({
1346
+ command: getCommandForHarness(harness),
1347
+ args: [],
1348
+ cwd,
1349
+ });
1350
+ }, [data, teamName]);
1351
+
1247
1352
  // Recovery: after HMR, all mounted TeamDetailView effects re-run simultaneously.
1248
1353
  // With CSS display-toggle (all tabs stay mounted), the last selectTeam() call wins
1249
1354
  // and other tabs get stuck with mismatched data (permanent skeleton).
@@ -1352,6 +1457,38 @@ export const TeamDetailView = ({
1352
1457
  teamName,
1353
1458
  ]);
1354
1459
 
1460
+ useEffect(() => {
1461
+ if (!teamName) return;
1462
+ let cancelled = false;
1463
+ setCcSessions([]);
1464
+
1465
+ const loadCcSessions = async (showLoading: boolean): Promise<void> => {
1466
+ if (showLoading) setCcSessionsLoading(true);
1467
+ setCcSessionsError(null);
1468
+ try {
1469
+ const result = await api.teams.getTeamSessions(teamName);
1470
+ if (!cancelled) setCcSessions(result);
1471
+ } catch (e) {
1472
+ if (!cancelled) {
1473
+ setCcSessions([]);
1474
+ setCcSessionsError(e instanceof Error ? e.message : '加载运行会话失败');
1475
+ }
1476
+ } finally {
1477
+ if (!cancelled && showLoading) setCcSessionsLoading(false);
1478
+ }
1479
+ };
1480
+
1481
+ void loadCcSessions(true);
1482
+ const interval = window.setInterval(() => {
1483
+ void loadCcSessions(false);
1484
+ }, 3_000);
1485
+
1486
+ return () => {
1487
+ cancelled = true;
1488
+ window.clearInterval(interval);
1489
+ };
1490
+ }, [teamName, data?.isAlive]);
1491
+
1355
1492
  useEffect(() => {
1356
1493
  if (!projectId) return;
1357
1494
 
@@ -1579,8 +1716,21 @@ export const TeamDetailView = ({
1579
1716
  }, []);
1580
1717
 
1581
1718
  const handleRestartTeam = useCallback(() => {
1719
+ const cfg = data?.config;
1720
+ const harness = cfg?.agentType || data?.harness;
1721
+ if (isLocalHarness(harness)) {
1722
+ const cwd = cfg?.projectPath || cfg?.executionTarget?.cwd || data?.workDir || '';
1723
+ if (cwd) {
1724
+ terminalPaneRef.current?.spawn({
1725
+ command: getCommandForHarness(harness),
1726
+ args: [],
1727
+ cwd,
1728
+ });
1729
+ return;
1730
+ }
1731
+ }
1582
1732
  openLaunchDialog('relaunch');
1583
- }, [openLaunchDialog]);
1733
+ }, [data, openLaunchDialog]);
1584
1734
 
1585
1735
  const handleStartCcConnectTeam = useCallback(() => {
1586
1736
  void (async () => {
@@ -1588,7 +1738,6 @@ export const TeamDetailView = ({
1588
1738
  openLaunchDialog('launch');
1589
1739
  return;
1590
1740
  }
1591
- await api.ccSettings.restart();
1592
1741
  await api.teams.launchTeam({
1593
1742
  teamName,
1594
1743
  cwd: data.config.projectPath,
@@ -1629,7 +1778,6 @@ export const TeamDetailView = ({
1629
1778
  );
1630
1779
 
1631
1780
  const handleRestartTeamFromEdit = useCallback(async (): Promise<void> => {
1632
- await api.ccSettings.restart();
1633
1781
  await Promise.all([fetchTeams(), selectTeam(teamName)]);
1634
1782
  }, [fetchTeams, selectTeam, teamName]);
1635
1783
 
@@ -1675,12 +1823,12 @@ export const TeamDetailView = ({
1675
1823
 
1676
1824
  const handleSelectMember = useCallback((member: ResolvedTeamMember) => {
1677
1825
  setSelectedMember(member);
1678
- setSelectedMemberView(null);
1826
+ setSelectedMember(null);
1679
1827
  }, []);
1680
1828
 
1681
1829
  const closeSelectedMemberDialog = useCallback(() => {
1682
1830
  setSelectedMember(null);
1683
- setSelectedMemberView(null);
1831
+ setSelectedMember(null);
1684
1832
  }, []);
1685
1833
 
1686
1834
  const handleSendMessageToMember = useCallback((member: ResolvedTeamMember) => {
@@ -1769,7 +1917,7 @@ export const TeamDetailView = ({
1769
1917
  const member = membersWithLiveBranches.find((m) => m.name === pendingMemberProfile);
1770
1918
  if (member) {
1771
1919
  setSelectedMember(member);
1772
- setSelectedMemberView(null);
1920
+ setSelectedMember(null);
1773
1921
  }
1774
1922
  useStore.getState().closeMemberProfile();
1775
1923
  }, [pendingMemberProfile, membersWithLiveBranches]);
@@ -1835,9 +1983,6 @@ export const TeamDetailView = ({
1835
1983
  void (async () => {
1836
1984
  try {
1837
1985
  const result = await deleteTeam(teamName);
1838
- if (result.restartRequired) {
1839
- await api.ccSettings.restart();
1840
- }
1841
1986
  await fetchTeams();
1842
1987
  setDeleteConfirmOpen(false);
1843
1988
  if (tabId) closeTab(tabId);
@@ -1904,7 +2049,7 @@ export const TeamDetailView = ({
1904
2049
  tasks: data?.tasks ?? [],
1905
2050
  isTeamAlive: data?.isAlive,
1906
2051
  timeWindow,
1907
- teamSessionIds,
2052
+ sessions: ccSessions,
1908
2053
  currentLeadSessionId: data?.config.leadSessionId,
1909
2054
  pendingRepliesByMember,
1910
2055
  onPendingReplyChange: setPendingRepliesByMember,
@@ -1919,12 +2064,12 @@ export const TeamDetailView = ({
1919
2064
  data?.config.leadSessionId,
1920
2065
  data?.isAlive,
1921
2066
  data?.tasks,
2067
+ ccSessions,
1922
2068
  handleReplyToMessage,
1923
2069
  handleRestartTeam,
1924
2070
  handleSelectMember,
1925
2071
  pendingRepliesByMember,
1926
2072
  teamName,
1927
- teamSessionIds,
1928
2073
  timeWindow,
1929
2074
  keepMessagesInline,
1930
2075
  ]
@@ -1987,6 +2132,7 @@ export const TeamDetailView = ({
1987
2132
  if (error === 'TEAM_DRAFT') {
1988
2133
  const draftTeamSummary = useStore.getState().teamByName[teamName];
1989
2134
  const draftDisplayName = draftTeamSummary?.displayName || teamName;
2135
+ const draftCwd = draftTeamSummary?.projectPath || draftTeamSummary?.workDir || '';
1990
2136
 
1991
2137
  return (
1992
2138
  <>
@@ -1994,44 +2140,27 @@ export const TeamDetailView = ({
1994
2140
  <div ref={provisioningBannerRef}>
1995
2141
  <TeamProvisioningBanner teamName={teamName} />
1996
2142
  </div>
1997
- <div className="flex min-h-[calc(100vh-12rem)] items-center justify-center">
1998
- <div className="max-w-md text-center">
1999
- <p className="text-sm font-medium text-text">团队尚未启动</p>
2000
- <p className="mt-2 text-xs text-text-secondary">
2001
- 这是一个草稿团队。<strong>{draftDisplayName}</strong>{' '}
2002
- 尚未完成启动,点击“启动团队”后即可选择模型并进入动态编排。
2003
- </p>
2004
- <div className="mt-4 flex justify-center gap-2">
2005
- <button
2006
- className="rounded-md bg-blue-600 px-4 py-1.5 text-xs font-medium text-white transition-colors hover:bg-blue-500"
2007
- onClick={() => openLaunchDialog('launch')}
2008
- >
2009
- 启动团队
2010
- </button>
2011
- <button
2012
- className="rounded-md bg-surface-raised px-4 py-1.5 text-xs font-medium text-text-secondary transition-colors hover:text-text"
2013
- onClick={() => {
2014
- void api.teams.deleteDraft(teamName).catch(() => {});
2015
- }}
2016
- >
2017
- 删除
2018
- </button>
2019
- </div>
2020
- </div>
2143
+ <div className="flex items-center gap-3 py-2">
2144
+ <p className="text-sm font-medium text-text">{draftDisplayName}</p>
2145
+ <span className="rounded-full bg-blue-500/20 px-2 py-0.5 text-[10px] font-medium text-blue-400">
2146
+ {getCommandForHarness()}
2147
+ </span>
2148
+ </div>
2149
+ <div className="h-[calc(100vh-16rem)] overflow-hidden rounded-lg border border-[var(--color-border-subtle)]">
2150
+ <TerminalPane
2151
+ ref={terminalPaneRef}
2152
+ autoSpawn={
2153
+ draftCwd
2154
+ ? {
2155
+ command: getCommandForHarness(),
2156
+ args: [],
2157
+ cwd: draftCwd,
2158
+ }
2159
+ : undefined
2160
+ }
2161
+ />
2021
2162
  </div>
2022
2163
  </div>
2023
- <LaunchTeamDialog
2024
- mode={launchDialogState.mode}
2025
- open={launchDialogOpen}
2026
- teamName={teamName}
2027
- members={[]}
2028
- defaultProjectPath={draftTeamSummary?.projectPath}
2029
- provisioningError={provisioningError}
2030
- clearProvisioningError={clearProvisioningError}
2031
- onClose={closeLaunchDialog}
2032
- onLaunch={handleLaunchDialogSubmit}
2033
- onRelaunch={handleRelaunchDialogSubmit}
2034
- />
2035
2164
  </>
2036
2165
  );
2037
2166
  }
@@ -2108,13 +2237,16 @@ export const TeamDetailView = ({
2108
2237
  <h2 className="text-base font-semibold text-[var(--color-text)]">
2109
2238
  {data.config.name}
2110
2239
  </h2>
2111
- {data.isAlive && (
2112
- <span className="inline-flex items-center gap-1 rounded-full bg-emerald-500/15 px-1.5 py-0.5 text-[10px] font-medium text-emerald-400">
2240
+ {data.platforms?.filter((pl) => pl.type !== 'bridge').map((pl) => (
2241
+ <span
2242
+ key={pl.type}
2243
+ className="inline-flex items-center gap-1 rounded-full bg-emerald-500/15 px-1.5 py-0.5 text-[10px] font-medium text-emerald-400"
2244
+ >
2113
2245
  <span className="size-1.5 rounded-full bg-emerald-400" />
2114
- 运行中
2246
+ {pl.type}
2115
2247
  </span>
2116
- )}
2117
- {!data.isAlive && isTeamProvisioning && (
2248
+ ))}
2249
+ {isTeamProvisioning && (
2118
2250
  <span className="inline-flex items-center gap-1 rounded-full bg-yellow-500/15 px-1.5 py-0.5 text-[10px] font-medium text-yellow-400">
2119
2251
  <span className="size-1.5 animate-pulse rounded-full bg-yellow-400" />
2120
2252
  启动中...
@@ -2123,6 +2255,18 @@ export const TeamDetailView = ({
2123
2255
  </div>
2124
2256
  </div>
2125
2257
  <div className="flex shrink-0 items-center gap-1.5">
2258
+ {teamName !== SYSTEM_MANAGER_TEAM_NAME ? (
2259
+ <Button
2260
+ variant="outline"
2261
+ size="sm"
2262
+ className="h-7 gap-1.5 px-2.5 text-xs text-[var(--color-text-secondary)]"
2263
+ disabled={isTeamProvisioning}
2264
+ onClick={() => setBindingDialogOpen(true)}
2265
+ >
2266
+ <Link size={12} />
2267
+ 运行时
2268
+ </Button>
2269
+ ) : null}
2126
2270
  <Button
2127
2271
  variant="outline"
2128
2272
  size="sm"
@@ -2144,32 +2288,21 @@ export const TeamDetailView = ({
2144
2288
  </Button>
2145
2289
  </PopoverTrigger>
2146
2290
  <PopoverContent align="end" className="w-44 p-1">
2147
- {data.config.projectPath && (
2148
- <button
2149
- type="button"
2150
- className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-xs text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-raised)] hover:text-[var(--color-text)]"
2151
- onClick={() => {
2152
- setHeaderMenuOpen(false);
2153
- setEnvDialogOpen(true);
2154
- }}
2155
- >
2156
- <Shield size={13} />
2157
- 环境变量
2158
- </button>
2159
- )}
2160
- {teamName !== 'default' && teamName !== 'my-project' && (
2161
- <button
2162
- type="button"
2163
- className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-xs text-red-400 hover:bg-red-500/10"
2164
- onClick={() => {
2165
- setHeaderMenuOpen(false);
2166
- handleDeleteTeam();
2167
- }}
2168
- >
2169
- <Trash2 size={13} />
2170
- 删除团队
2171
- </button>
2172
- )}
2291
+ {teamName !== 'default' &&
2292
+ teamName !== 'my-project' &&
2293
+ teamName !== SYSTEM_MANAGER_TEAM_NAME && (
2294
+ <button
2295
+ type="button"
2296
+ className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-xs text-red-400 hover:bg-red-500/10"
2297
+ onClick={() => {
2298
+ setHeaderMenuOpen(false);
2299
+ handleDeleteTeam();
2300
+ }}
2301
+ >
2302
+ <Trash2 size={13} />
2303
+ 删除团队
2304
+ </button>
2305
+ )}
2173
2306
  </PopoverContent>
2174
2307
  </Popover>
2175
2308
  </div>
@@ -2279,6 +2412,42 @@ export const TeamDetailView = ({
2279
2412
  />
2280
2413
  </CollapsibleTeamSection>
2281
2414
 
2415
+ <CollapsibleTeamSection
2416
+ sectionId="team-sessions"
2417
+ title="会话"
2418
+ icon={<MessageSquare size={14} />}
2419
+ badge={ccSessions.length}
2420
+ defaultOpen={ccSessions.length > 0}
2421
+ action={
2422
+ ccSessions.length > 0 ? (
2423
+ <button
2424
+ type="button"
2425
+ className="inline-flex items-center gap-1 rounded-md px-2 py-0.5 text-[11px] text-[var(--color-text-muted)] transition-colors hover:bg-[var(--color-surface-raised)] hover:text-[var(--color-text)] disabled:cursor-not-allowed disabled:opacity-50"
2426
+ onClick={(e) => {
2427
+ e.stopPropagation();
2428
+ void handleExportAllCcSessions();
2429
+ }}
2430
+ disabled={ccExporting}
2431
+ title="导出所有会话为 CSV 表格"
2432
+ >
2433
+ {ccExporting ? (
2434
+ <Loader2 size={12} className="animate-spin" />
2435
+ ) : (
2436
+ <Download size={12} />
2437
+ )}
2438
+ 导出
2439
+ </button>
2440
+ ) : undefined
2441
+ }
2442
+ >
2443
+ <CcSessionsSection
2444
+ teamName={teamName}
2445
+ sessions={ccSessions}
2446
+ loading={ccSessionsLoading}
2447
+ error={ccSessionsError}
2448
+ />
2449
+ </CollapsibleTeamSection>
2450
+
2282
2451
  <CollapsibleTeamSection
2283
2452
  sectionId="kanban"
2284
2453
  title="外部派单"
@@ -2472,8 +2641,6 @@ export const TeamDetailView = ({
2472
2641
  </div>
2473
2642
  </CollapsibleTeamSection>
2474
2643
 
2475
- <TeamMessagesPanelBridge position="inline" {...sharedMessagesPanelProps} />
2476
-
2477
2644
  {(data.processes?.length ?? 0) > 0 && (
2478
2645
  <CollapsibleTeamSection
2479
2646
  sectionId="processes"
@@ -2532,8 +2699,6 @@ export const TeamDetailView = ({
2532
2699
  teamName={teamName}
2533
2700
  members={membersWithLiveBranches}
2534
2701
  tasks={data.tasks}
2535
- initialTab={selectedMemberView?.initialTab}
2536
- initialActivityFilter={selectedMemberView?.initialActivityFilter}
2537
2702
  isTeamAlive={data.isAlive}
2538
2703
  isTeamProvisioning={isTeamProvisioning}
2539
2704
  launchParams={launchParams}
@@ -2580,18 +2745,6 @@ export const TeamDetailView = ({
2580
2745
  }}
2581
2746
  />
2582
2747
 
2583
- <Dialog open={envDialogOpen} onOpenChange={setEnvDialogOpen}>
2584
- <DialogContent className="max-h-[80vh] max-w-lg overflow-y-auto">
2585
- <DialogHeader>
2586
- <DialogTitle>项目环境变量</DialogTitle>
2587
- <DialogDescription>
2588
- 管理当前项目所需的环境变量,供 MCP 和 Skills 使用。
2589
- </DialogDescription>
2590
- </DialogHeader>
2591
- <ProjectEnvPanel projectPath={data.config.projectPath ?? null} />
2592
- </DialogContent>
2593
- </Dialog>
2594
-
2595
2748
  <Dialog
2596
2749
  open={removeMemberConfirm !== null}
2597
2750
  onOpenChange={(open) => {
@@ -2770,14 +2923,72 @@ export const TeamDetailView = ({
2770
2923
  {spawnStatusWatcher}
2771
2924
  {teamAgentRuntimeWatcher}
2772
2925
  {leadContextWatcher}
2773
- {renderBody()}
2926
+ <div className="flex size-full flex-col overflow-hidden">
2927
+ <div className="min-h-0 flex-1 overflow-hidden">
2928
+ {renderBody()}
2929
+ </div>
2930
+ {/* Persistent terminal — survives data refreshes because it's outside renderBody() */}
2931
+ {teamName && teamName !== SYSTEM_MANAGER_TEAM_NAME && (
2932
+ <div className="shrink-0" style={{ height: terminalHeight }}>
2933
+ {/* Drag handle */}
2934
+ <div
2935
+ className="flex cursor-row-resize items-center justify-center border-t border-[var(--color-border)] py-0.5 transition-colors hover:bg-[var(--color-surface-raised)]"
2936
+ onMouseDown={(e) => {
2937
+ e.preventDefault();
2938
+ const startY = e.clientY;
2939
+ const startH = terminalHeight;
2940
+ const onMove = (ev: MouseEvent) => {
2941
+ const delta = startY - ev.clientY; // drag up = grow
2942
+ const next = Math.max(120, Math.min(800, startH + delta));
2943
+ setTerminalHeight(next);
2944
+ };
2945
+ const onUp = () => {
2946
+ window.removeEventListener('mousemove', onMove);
2947
+ window.removeEventListener('mouseup', onUp);
2948
+ };
2949
+ window.addEventListener('mousemove', onMove);
2950
+ window.addEventListener('mouseup', onUp);
2951
+ }}
2952
+ >
2953
+ <div className="h-1 w-8 rounded-full bg-[var(--color-border)]" />
2954
+ </div>
2955
+ <div className="h-[calc(100%-14px)] overflow-hidden">
2956
+ <div className="flex items-center justify-between px-1 pb-1">
2957
+ <div className="flex items-center gap-1.5 text-xs font-medium text-[var(--color-text-secondary)]">
2958
+ <Terminal size={14} />
2959
+ 终端
2960
+ </div>
2961
+ <button
2962
+ type="button"
2963
+ className="flex items-center gap-1 rounded px-1 py-0.5 text-[10px] text-[var(--color-text-muted)] transition-colors hover:bg-[var(--color-surface-raised)] hover:text-[var(--color-text-secondary)]"
2964
+ onClick={() => {
2965
+ const cwd = data?.config.projectPath || data?.config.executionTarget?.cwd || data?.workDir || '';
2966
+ const harness = data?.config.agentType || data?.harness;
2967
+ void api.terminal.openExternal({ command: getCommandForHarness(harness), cwd });
2968
+ }}
2969
+ >
2970
+ <Play size={10} />
2971
+ 外部终端
2972
+ </button>
2973
+ </div>
2974
+ <div className="h-[calc(100%-22px)] overflow-hidden rounded-lg border border-[var(--color-border-subtle)]">
2975
+ <TerminalPane ref={terminalPaneRef} />
2976
+ </div>
2977
+ </div>
2978
+ </div>
2979
+ )}
2980
+ </div>
2981
+ {data && teamName !== SYSTEM_MANAGER_TEAM_NAME ? (
2982
+ <RuntimeConfigDialog
2983
+ open={bindingDialogOpen}
2984
+ teamName={teamName}
2985
+ onClose={() => setBindingDialogOpen(false)}
2986
+ />
2987
+ ) : null}
2774
2988
  <EditTeamDialog
2775
2989
  open={editDialogOpen}
2776
2990
  teamName={teamName}
2777
2991
  onClose={() => setEditDialogOpen(false)}
2778
- onDeleteTeam={
2779
- teamName !== 'default' && teamName !== 'my-project' ? handleDeleteTeam : undefined
2780
- }
2781
2992
  />
2782
2993
  </>
2783
2994
  );