@yancyyu/openhermit 1.6.38 → 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 (188) hide show
  1. package/dist-renderer/assets/ProjectEditorOverlay-krO5vQxX.js +58 -0
  2. package/dist-renderer/assets/{TeamGraphOverlay-ZEDfZyHb.js → TeamGraphOverlay-DqhQzcTr.js} +1 -1
  3. package/dist-renderer/assets/{_basePickBy-CIhniz70.js → _basePickBy-B7kSYPxr.js} +1 -1
  4. package/dist-renderer/assets/{_baseUniq-cKAW4Q8I.js → _baseUniq-CnjxqwAk.js} +1 -1
  5. package/dist-renderer/assets/{arc-YmNsoDXW.js → arc-CLeZuINP.js} +1 -1
  6. package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-DHEls2sX.js → architectureDiagram-VXUJARFQ-QKtqaqdY.js} +1 -1
  7. package/dist-renderer/assets/{blockDiagram-VD42YOAC-Bpwf1Sbg.js → blockDiagram-VD42YOAC-BqdrzO_f.js} +1 -1
  8. package/dist-renderer/assets/{c4Diagram-YG6GDRKO-B0IaQ4w5.js → c4Diagram-YG6GDRKO-gwPlCxDC.js} +1 -1
  9. package/dist-renderer/assets/channel-DpMHF50r.js +1 -0
  10. package/dist-renderer/assets/{chunk-4BX2VUAB-DLk-hcFc.js → chunk-4BX2VUAB-C6XLurL4.js} +1 -1
  11. package/dist-renderer/assets/{chunk-55IACEB6-1XRmX_Zm.js → chunk-55IACEB6-Ds6quhEP.js} +1 -1
  12. package/dist-renderer/assets/{chunk-B4BG7PRW-1waH1DAD.js → chunk-B4BG7PRW-5UlA1_e9.js} +1 -1
  13. package/dist-renderer/assets/{chunk-DI55MBZ5-BqpZBtrN.js → chunk-DI55MBZ5-ywFrqIsY.js} +1 -1
  14. package/dist-renderer/assets/{chunk-FMBD7UC4-Bly7vVym.js → chunk-FMBD7UC4-C7ifUA17.js} +1 -1
  15. package/dist-renderer/assets/{chunk-QN33PNHL-Ci2QWBAs.js → chunk-QN33PNHL-BxGCo80U.js} +1 -1
  16. package/dist-renderer/assets/{chunk-QZHKN3VN-YCqFW7d-.js → chunk-QZHKN3VN-B2CuaZs6.js} +1 -1
  17. package/dist-renderer/assets/{chunk-TZMSLE5B-B0xGXInl.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-DxcFNQKT.js → cose-bilkent-S5V4N54A-Cz1GVtLp.js} +1 -1
  22. package/dist-renderer/assets/{dagre-6UL2VRFP-DPo_RfZY.js → dagre-6UL2VRFP-BrmR-P4h.js} +1 -1
  23. package/dist-renderer/assets/{diagram-PSM6KHXK-U3hQsFe4.js → diagram-PSM6KHXK-DbNjC5Rg.js} +1 -1
  24. package/dist-renderer/assets/{diagram-QEK2KX5R-OrwrAy0V.js → diagram-QEK2KX5R-qkRX5_Mq.js} +1 -1
  25. package/dist-renderer/assets/{diagram-S2PKOQOG-CXATPWVw.js → diagram-S2PKOQOG-CyL5rCv2.js} +1 -1
  26. package/dist-renderer/assets/{erDiagram-Q2GNP2WA-B0e8AfMF.js → erDiagram-Q2GNP2WA-Dox3-bA5.js} +1 -1
  27. package/dist-renderer/assets/{flowDiagram-NV44I4VS-CXfzA4jJ.js → flowDiagram-NV44I4VS-BtkaxlDL.js} +1 -1
  28. package/dist-renderer/assets/{ganttDiagram-JELNMOA3-CMr08qVl.js → ganttDiagram-JELNMOA3-Dhy_d9GK.js} +1 -1
  29. package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-vYFHpPmy.js → gitGraphDiagram-V2S2FVAM-B5XRhIQA.js} +1 -1
  30. package/dist-renderer/assets/{graph-DOe5j8dH.js → graph-CsoEwUhS.js} +1 -1
  31. package/dist-renderer/assets/{index-BySQS7AB.js → index-BWPWmJNo.js} +1 -1
  32. package/dist-renderer/assets/{index-V7dAKPqd.js → index-Bu2R-Se7.js} +587 -705
  33. package/dist-renderer/assets/index-CnWV3BhG.css +32 -0
  34. package/dist-renderer/assets/{index-CzWxVCRL.js → index-D-3KgskL.js} +1 -1
  35. package/dist-renderer/assets/{index-VJ-MM9xa.js → index-DGEBzLNT.js} +1 -1
  36. package/dist-renderer/assets/{index-B2Dy7M2G.js → index-NhHNs2Oo.js} +1 -1
  37. package/dist-renderer/assets/{index-C_okzZXP.js → index-h17WuEyf.js} +1 -1
  38. package/dist-renderer/assets/{infoDiagram-HS3SLOUP-D_WubR0B.js → infoDiagram-HS3SLOUP-hMGmNojH.js} +1 -1
  39. package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-w9ca-1TI.js → journeyDiagram-XKPGCS4Q-DXV2rBDl.js} +1 -1
  40. package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-Jg9p6_pN.js → kanban-definition-3W4ZIXB7-Bf99WLRy.js} +1 -1
  41. package/dist-renderer/assets/{layout-B-z3y17c.js → layout-C3XWrpwo.js} +1 -1
  42. package/dist-renderer/assets/{linear-D-RTX5UW.js → linear-OEEcn8KN.js} +1 -1
  43. package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-CDQmHOYP.js → mindmap-definition-VGOIOE7T-Dpi3S2x4.js} +1 -1
  44. package/dist-renderer/assets/{pieDiagram-ADFJNKIX-D_odsQL7.js → pieDiagram-ADFJNKIX-xTPPhtNx.js} +1 -1
  45. package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-BRsmYWSA.js → quadrantDiagram-AYHSOK5B-euniyDlz.js} +1 -1
  46. package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-ChNE_BOV.js → requirementDiagram-UZGBJVZJ-D9Uiw4kF.js} +1 -1
  47. package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-C8FtpwKc.js → sankeyDiagram-TZEHDZUN-CySU4nED.js} +1 -1
  48. package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-DmLCzNcc.js → sequenceDiagram-WL72ISMW-JVGpET6V.js} +1 -1
  49. package/dist-renderer/assets/splashScene-D0YB9uxm.js +17 -0
  50. package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-WJBm4bhu.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-BXs_hOJs.js → timeline-definition-IT6M3QCI-DmycNUUe.js} +1 -1
  53. package/dist-renderer/assets/{treemap-GDKQZRPO-o04MA0G9.js → treemap-GDKQZRPO-DPq4gZuB.js} +1 -1
  54. package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-Czj69XRd.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 +2 -1
  58. package/src/main/server.ts +873 -221
  59. package/src/main/services/extensions/ExtensionFacadeService.ts +2 -5
  60. package/src/main/services/extensions/catalog/PluginCatalogService.ts +4 -2
  61. package/src/main/services/session-intelligence/ConversationTelemetryService.ts +1101 -0
  62. package/src/main/services/session-intelligence/LocalSessionScanner.ts +512 -0
  63. package/src/main/services/session-intelligence/SessionUsageParser.ts +4 -4
  64. package/src/main/services/system-manager/SystemManagerConfigService.ts +122 -0
  65. package/src/main/services/system-manager/SystemManagerPtyService.ts +233 -0
  66. package/src/main/services/system-manager/WorkflowPromptService.ts +75 -0
  67. package/src/main/services/teams-mvp/TaskDispatchService.ts +5 -6
  68. package/src/main/services/teams-mvp/TeamProvisioningService.ts +39 -2
  69. package/src/main/services/teams-mvp/TeamWorkspaceService.ts +22 -4
  70. package/src/main/utils/teamProjectResolution.ts +15 -0
  71. package/src/renderer/App.tsx +8 -4
  72. package/src/renderer/api/httpClient.ts +68 -18
  73. package/src/renderer/api/providers.ts +23 -2
  74. package/src/renderer/assets/participant-avatars/01.svg +3 -0
  75. package/src/renderer/assets/participant-avatars/02.svg +3 -0
  76. package/src/renderer/assets/participant-avatars/03.svg +3 -0
  77. package/src/renderer/assets/participant-avatars/04.svg +3 -0
  78. package/src/renderer/assets/participant-avatars/05.svg +3 -0
  79. package/src/renderer/assets/participant-avatars/06.svg +3 -0
  80. package/src/renderer/assets/participant-avatars/07.svg +3 -0
  81. package/src/renderer/assets/participant-avatars/08.svg +3 -0
  82. package/src/renderer/assets/participant-avatars/09.svg +3 -0
  83. package/src/renderer/assets/participant-avatars/10.svg +3 -0
  84. package/src/renderer/assets/participant-avatars/11.svg +3 -0
  85. package/src/renderer/assets/participant-avatars/12.svg +3 -0
  86. package/src/renderer/assets/participant-avatars/13.svg +3 -0
  87. package/src/renderer/components/common/TerminalPane.tsx +213 -0
  88. package/src/renderer/components/dashboard/DashboardView.tsx +9 -36
  89. package/src/renderer/components/extensions/ExtensionStoreView.tsx +6 -125
  90. package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +1 -1
  91. package/src/renderer/components/extensions/mcp/McpLibraryEnableDialog.tsx +305 -0
  92. package/src/renderer/components/extensions/mcp/McpLibraryEntryDialog.tsx +418 -0
  93. package/src/renderer/components/extensions/mcp/McpLibraryPanel.tsx +404 -0
  94. package/src/renderer/components/extensions/plugins/PluginCard.tsx +6 -6
  95. package/src/renderer/components/extensions/plugins/PluginsPanel.tsx +34 -21
  96. package/src/renderer/components/extensions/skills/SkillsLibraryPanel.tsx +335 -0
  97. package/src/renderer/components/layout/PaneContent.tsx +8 -1
  98. package/src/renderer/components/layout/Sidebar.tsx +11 -54
  99. package/src/renderer/components/layout/SortableTab.tsx +20 -31
  100. package/src/renderer/components/layout/TabBar.tsx +1 -1
  101. package/src/renderer/components/layout/TabContextMenu.tsx +1 -1
  102. package/src/renderer/components/runtime/ProviderRuntimeSettingsDialog.tsx +768 -157
  103. package/src/renderer/components/schedules/SchedulesView.tsx +51 -462
  104. package/src/renderer/components/schedules/calendar/CalendarDayView.tsx +173 -0
  105. package/src/renderer/components/schedules/calendar/CalendarEventBlock.tsx +113 -0
  106. package/src/renderer/components/schedules/calendar/CalendarHeader.tsx +148 -0
  107. package/src/renderer/components/schedules/calendar/CalendarMonthView.tsx +142 -0
  108. package/src/renderer/components/schedules/calendar/CalendarWeekView.tsx +219 -0
  109. package/src/renderer/components/schedules/calendar/ScheduleCalendarBoard.tsx +41 -0
  110. package/src/renderer/components/schedules/calendar/TeamGanttView.tsx +405 -0
  111. package/src/renderer/components/schedules/calendar/computeOccurrences.ts +234 -0
  112. package/src/renderer/components/schedules/calendar/index.ts +2 -0
  113. package/src/renderer/components/schedules/calendar/types.ts +44 -0
  114. package/src/renderer/components/settings/SettingsTabs.tsx +50 -55
  115. package/src/renderer/components/settings/SettingsView.tsx +30 -35
  116. package/src/renderer/components/settings/components/SettingsSectionHeader.tsx +5 -1
  117. package/src/renderer/components/settings/components/SettingsSelect.tsx +5 -3
  118. package/src/renderer/components/settings/components/SettingsToggle.tsx +2 -2
  119. package/src/renderer/components/settings/sections/AdvancedSection.tsx +11 -42
  120. package/src/renderer/components/settings/sections/CliStatusSection.tsx +71 -112
  121. package/src/renderer/components/settings/sections/ConfigEditorDialog.tsx +1 -1
  122. package/src/renderer/components/settings/sections/GeneralSection.tsx +11 -3
  123. package/src/renderer/components/settings/sections/HarnessSection.tsx +18 -14
  124. package/src/renderer/components/settings/sections/PlatformsSection.tsx +3 -3
  125. package/src/renderer/components/settings/sections/TaskBusSection.tsx +33 -40
  126. package/src/renderer/components/settings/sections/index.ts +0 -1
  127. package/src/renderer/components/sidebar/SidebarSessions.tsx +182 -4
  128. package/src/renderer/components/sidebar/SidebarTaskItem.tsx +4 -4
  129. package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +39 -4
  130. package/src/renderer/components/splash/splashScene.ts +121 -929
  131. package/src/renderer/components/system-manager/FolderBrowser.tsx +163 -0
  132. package/src/renderer/components/system-manager/SystemManagerView.tsx +351 -0
  133. package/src/renderer/components/tasks/TasksView.tsx +112 -134
  134. package/src/renderer/components/team/CcSessionsSection.tsx +431 -89
  135. package/src/renderer/components/team/CollapsibleTeamSection.tsx +17 -32
  136. package/src/renderer/components/team/TeamDetailView.tsx +319 -123
  137. package/src/renderer/components/team/TeamListView.tsx +108 -123
  138. package/src/renderer/components/team/dialogs/CreateTaskDialog.tsx +2 -2
  139. package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +84 -306
  140. package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +259 -342
  141. package/src/renderer/components/team/dialogs/GlobalTaskDetailDialog.tsx +1 -1
  142. package/src/renderer/components/team/dialogs/LaunchTeamDialog.tsx +17 -15
  143. package/src/renderer/components/team/dialogs/PlatformBindingDialog.tsx +221 -0
  144. package/src/renderer/components/team/dialogs/PlatformManualForm.tsx +7 -0
  145. package/src/renderer/components/team/dialogs/PlatformSetupQR.tsx +1 -1
  146. package/src/renderer/components/team/dialogs/RuntimeConfigDialog.tsx +361 -0
  147. package/src/renderer/components/team/dialogs/platformMeta.ts +122 -11
  148. package/src/renderer/components/team/dialogs/useTeamEditForm.ts +17 -5
  149. package/src/renderer/components/team/kanban/KanbanBoard.tsx +9 -9
  150. package/src/renderer/components/team/members/MemberCard.tsx +14 -47
  151. package/src/renderer/components/team/members/MemberDetailDialog.tsx +3 -95
  152. package/src/renderer/components/team/members/MemberDetailStats.tsx +50 -65
  153. package/src/renderer/components/team/messages/MessageComposer.tsx +8 -110
  154. package/src/renderer/components/team/messages/MessagesPanel.tsx +131 -114
  155. package/src/renderer/components/team/schedule/ScheduleStatusBadge.tsx +2 -2
  156. package/src/renderer/components/team/tools/AddMcpInline.tsx +27 -17
  157. package/src/renderer/components/team/tools/McpChip.tsx +6 -3
  158. package/src/renderer/components/team/tools/SkillChip.tsx +2 -2
  159. package/src/renderer/components/team/tools/ToolsSection.tsx +418 -70
  160. package/src/renderer/hooks/useExtensionsTabState.ts +3 -114
  161. package/src/renderer/index.css +39 -22
  162. package/src/renderer/index.html +17 -50
  163. package/src/renderer/store/index.ts +2 -1
  164. package/src/renderer/store/slices/scheduleSlice.ts +1 -1
  165. package/src/renderer/store/slices/teamSlice.ts +45 -168
  166. package/src/renderer/utils/claudeCodeOnlyProviders.ts +3 -10
  167. package/src/renderer/utils/memberHelpers.ts +5 -17
  168. package/src/renderer/utils/openCodeRuntimeDeliveryDiagnostics.ts +4 -2
  169. package/src/renderer/utils/providerSlashCommands.ts +0 -5
  170. package/src/renderer/utils/scheduleFormatters.ts +3 -1
  171. package/src/renderer/utils/teamMessageFiltering.ts +14 -1
  172. package/src/renderer/utils/teamModelAvailability.ts +18 -2
  173. package/src/shared/types/api.ts +121 -2
  174. package/src/shared/types/ccConnect.ts +2 -0
  175. package/src/shared/types/index.ts +3 -0
  176. package/src/shared/types/systemManager.ts +49 -0
  177. package/src/shared/types/team.ts +29 -0
  178. package/src/shared/types/terminal.ts +4 -2
  179. package/src/shared/utils/extensionNormalizers.ts +15 -8
  180. package/src/shared/utils/providerExtensionCapabilities.ts +2 -2
  181. package/dist-renderer/assets/ProjectEditorOverlay-lJZi-9Hp.js +0 -52
  182. package/dist-renderer/assets/channel-yIlSKy0e.js +0 -1
  183. package/dist-renderer/assets/classDiagram-2ON5EDUG-24fHez0s.js +0 -1
  184. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-24fHez0s.js +0 -1
  185. package/dist-renderer/assets/clone-BTNuUva-.js +0 -1
  186. package/dist-renderer/assets/index-Bi6nrZ4z.css +0 -1
  187. package/dist-renderer/assets/splashScene-C8lWNnm4.js +0 -1
  188. package/dist-renderer/assets/stateDiagram-v2-4FDKWEC3-_m6iPPUR.js +0 -1
@@ -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,16 +73,16 @@ import {
71
73
  Loader2,
72
74
  MessageSquare,
73
75
  MoreHorizontal,
74
- Shield,
75
76
  Users,
76
- Wrench,
77
77
  } from 'lucide-react';
78
78
  import { useShallow } from 'zustand/react/shallow';
79
79
 
80
80
  import { CreateTaskDialog } from './dialogs/CreateTaskDialog';
81
81
  import { EditTeamDialog } from './dialogs/EditTeamDialog';
82
82
  import { LaunchTeamDialog, type TeamLaunchDialogMode } from './dialogs/LaunchTeamDialog';
83
+ import { PlatformBindingDialog } from './dialogs/PlatformBindingDialog';
83
84
  import { ReviewDialog } from './dialogs/ReviewDialog';
85
+ import { RuntimeConfigDialog } from './dialogs/RuntimeConfigDialog';
84
86
  import { SendMessageDialog } from './dialogs/SendMessageDialog';
85
87
  import { TaskDetailDialog } from './dialogs/TaskDetailDialog';
86
88
  import { executeTeamRelaunch } from './dialogs/teamRelaunchFlow';
@@ -89,25 +91,26 @@ import { UNASSIGNED_OWNER } from './kanban/KanbanFilterPopover';
89
91
  import { KanbanSearchInput } from './kanban/KanbanSearchInput';
90
92
  import { TrashDialog } from './kanban/TrashDialog';
91
93
  import { MemberDetailDialog } from './members/MemberDetailDialog';
92
- import { type MemberActivityFilter, type MemberDetailTab } from './members/memberDetailTypes';
94
+
93
95
 
94
96
  import type { TeamMessagesPanelMode } from '@renderer/types/teamMessagesPanelMode';
95
97
  import type { ComponentProps } from 'react';
96
98
 
99
+ import { TerminalPane, type TerminalPaneRef } from '@renderer/components/common/TerminalPane';
100
+
97
101
  const ProjectEditorOverlay = lazy(() =>
98
102
  import('./editor/ProjectEditorOverlay').then((m) => ({ default: m.ProjectEditorOverlay }))
99
103
  );
100
104
  import { MemberList } from './members/MemberList';
101
105
  import { MessagesPanel } from './messages/MessagesPanel';
106
+ import { CcSessionsSection, buildAllSessionsCsv, buildAllSessionsCsvFilename, downloadTextFile, hasDataRows, isExportPayload } from './CcSessionsSection';
102
107
  import { ChangeReviewDialog } from './review/ChangeReviewDialog';
103
- import { ProjectEnvPanel } from '../extensions/env/ProjectEnvPanel';
104
108
  import {
105
109
  getTeamPendingRepliesState,
106
110
  setTeamPendingRepliesState,
107
111
  } from './sidebar/teamSidebarUiState';
108
112
  import { CollapsibleTeamSection } from './CollapsibleTeamSection';
109
113
  import { ProcessesSection } from './ProcessesSection';
110
- import { ToolsSection } from './tools/ToolsSection';
111
114
  import { getLaunchJoinMilestonesFromMembers, getLaunchJoinState } from './provisioningSteps';
112
115
  import { TeamProvisioningBanner } from './TeamProvisioningBanner';
113
116
  import {
@@ -121,6 +124,7 @@ import type { ContextInjection } from '@renderer/types/contextInjection';
121
124
  import type { Session } from '@renderer/types/data';
122
125
  import type { InlineChip } from '@renderer/types/inlineChip';
123
126
  import type {
127
+ CcSession,
124
128
  EffortLevel,
125
129
  GlobalProvider,
126
130
  MemberSpawnStatusEntry,
@@ -134,6 +138,7 @@ import type {
134
138
  TeamTaskWithKanban,
135
139
  TeamViewSnapshot,
136
140
  } from '@shared/types';
141
+ import { SYSTEM_MANAGER_TEAM_NAME } from '@shared/types/team';
137
142
  import type { EditorSelectionAction } from '@shared/types/editor';
138
143
  import type { ContextUsageLike } from '@shared/utils/contextMetrics';
139
144
 
@@ -234,7 +239,7 @@ const TeamOfflineStatusBanner = memo(function TeamOfflineStatusBanner({
234
239
  teamName: string;
235
240
  onLaunch: () => void;
236
241
  }): React.JSX.Element {
237
- const message = '团队离线中';
242
+ const message = '数字员工未运行:当前没有本地 Claude/Agent 进程在运行,已有数据和任务仍会保留。';
238
243
 
239
244
  return (
240
245
  <div
@@ -256,7 +261,7 @@ const TeamOfflineStatusBanner = memo(function TeamOfflineStatusBanner({
256
261
  onClick={onLaunch}
257
262
  >
258
263
  <Play size={12} />
259
- 启动
264
+ 启动数字员工
260
265
  </Button>
261
266
  </div>
262
267
  );
@@ -861,6 +866,43 @@ const TeamMemberDetailDialogBridge = memo(function TeamMemberDetailDialogBridge(
861
866
  );
862
867
  });
863
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
+
864
906
  export const TeamDetailView = ({
865
907
  teamName,
866
908
  isPaneFocused = false,
@@ -869,10 +911,6 @@ export const TeamDetailView = ({
869
911
  const [requestChangesTaskId, setRequestChangesTaskId] = useState<string | null>(null);
870
912
  const [selectedTask, setSelectedTask] = useState<TeamTaskWithKanban | null>(null);
871
913
  const [selectedMember, setSelectedMember] = useState<ResolvedTeamMember | null>(null);
872
- const [selectedMemberView, setSelectedMemberView] = useState<{
873
- initialTab?: MemberDetailTab;
874
- initialActivityFilter?: MemberActivityFilter;
875
- } | null>(null);
876
914
  const [pendingRepliesByMember, setPendingRepliesByMember] = useState<Record<string, number>>(() =>
877
915
  getTeamPendingRepliesState(teamName)
878
916
  );
@@ -886,7 +924,7 @@ export const TeamDetailView = ({
886
924
  const [removeMemberConfirm, setRemoveMemberConfirm] = useState<string | null>(null);
887
925
  const [updatingRoleLoading, setUpdatingRoleLoading] = useState(false);
888
926
  const [editDialogOpen, setEditDialogOpen] = useState(false);
889
- const [envDialogOpen, setEnvDialogOpen] = useState(false);
927
+ const [bindingDialogOpen, setBindingDialogOpen] = useState(false);
890
928
  const [headerMenuOpen, setHeaderMenuOpen] = useState(false);
891
929
  const [launchDialogState, setLaunchDialogState] = useState<{
892
930
  open: boolean;
@@ -896,8 +934,11 @@ export const TeamDetailView = ({
896
934
  mode: 'launch',
897
935
  });
898
936
  const [editorOpen, setEditorOpen] = useState(false);
937
+ const [terminalHeight, setTerminalHeight] = useState(400);
899
938
  const contentRef = useRef<HTMLDivElement>(null);
900
939
  const provisioningBannerRef = useRef<HTMLDivElement>(null);
940
+ const terminalPaneRef = useRef<TerminalPaneRef>(null);
941
+ const terminalSpawnedForTeamRef = useRef<string | null>(null);
901
942
  const wasProvisioningRef = useRef(false);
902
943
 
903
944
  // Set inert on background content when editor overlay is open (a11y focus trap)
@@ -931,17 +972,11 @@ export const TeamDetailView = ({
931
972
  const {
932
973
  teamName: tn,
933
974
  memberName,
934
- initialTab,
935
- initialActivityFilter,
936
975
  } = (e as CustomEvent).detail ?? {};
937
976
  if (tn !== teamName || !data) return;
938
977
  const member = members.find((m: { name: string }) => m.name === memberName);
939
978
  if (member) {
940
979
  setSelectedMember(member);
941
- setSelectedMemberView({
942
- initialTab,
943
- initialActivityFilter,
944
- });
945
980
  }
946
981
  };
947
982
  const onCreateTask = (e: Event) => {
@@ -1088,6 +1123,56 @@ export const TeamDetailView = ({
1088
1123
  const [sessions, setSessions] = useState<Session[]>([]);
1089
1124
  const [sessionsLoading, setSessionsLoading] = useState(false);
1090
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]);
1091
1176
  const [kanbanFilter, setKanbanFilter] = useState<KanbanFilterState>({
1092
1177
  sessionId: null,
1093
1178
  selectedOwners: new Set(),
@@ -1246,6 +1331,24 @@ export const TeamDetailView = ({
1246
1331
  void fetchDeletedTasks(teamName);
1247
1332
  }, [teamName, selectTeam, fetchDeletedTasks]);
1248
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
+
1249
1352
  // Recovery: after HMR, all mounted TeamDetailView effects re-run simultaneously.
1250
1353
  // With CSS display-toggle (all tabs stay mounted), the last selectTeam() call wins
1251
1354
  // and other tabs get stuck with mismatched data (permanent skeleton).
@@ -1354,6 +1457,38 @@ export const TeamDetailView = ({
1354
1457
  teamName,
1355
1458
  ]);
1356
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
+
1357
1492
  useEffect(() => {
1358
1493
  if (!projectId) return;
1359
1494
 
@@ -1581,8 +1716,21 @@ export const TeamDetailView = ({
1581
1716
  }, []);
1582
1717
 
1583
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
+ }
1584
1732
  openLaunchDialog('relaunch');
1585
- }, [openLaunchDialog]);
1733
+ }, [data, openLaunchDialog]);
1586
1734
 
1587
1735
  const handleStartCcConnectTeam = useCallback(() => {
1588
1736
  void (async () => {
@@ -1590,7 +1738,6 @@ export const TeamDetailView = ({
1590
1738
  openLaunchDialog('launch');
1591
1739
  return;
1592
1740
  }
1593
- await api.ccSettings.restart();
1594
1741
  await api.teams.launchTeam({
1595
1742
  teamName,
1596
1743
  cwd: data.config.projectPath,
@@ -1631,7 +1778,6 @@ export const TeamDetailView = ({
1631
1778
  );
1632
1779
 
1633
1780
  const handleRestartTeamFromEdit = useCallback(async (): Promise<void> => {
1634
- await api.ccSettings.restart();
1635
1781
  await Promise.all([fetchTeams(), selectTeam(teamName)]);
1636
1782
  }, [fetchTeams, selectTeam, teamName]);
1637
1783
 
@@ -1677,12 +1823,12 @@ export const TeamDetailView = ({
1677
1823
 
1678
1824
  const handleSelectMember = useCallback((member: ResolvedTeamMember) => {
1679
1825
  setSelectedMember(member);
1680
- setSelectedMemberView(null);
1826
+ setSelectedMember(null);
1681
1827
  }, []);
1682
1828
 
1683
1829
  const closeSelectedMemberDialog = useCallback(() => {
1684
1830
  setSelectedMember(null);
1685
- setSelectedMemberView(null);
1831
+ setSelectedMember(null);
1686
1832
  }, []);
1687
1833
 
1688
1834
  const handleSendMessageToMember = useCallback((member: ResolvedTeamMember) => {
@@ -1771,7 +1917,7 @@ export const TeamDetailView = ({
1771
1917
  const member = membersWithLiveBranches.find((m) => m.name === pendingMemberProfile);
1772
1918
  if (member) {
1773
1919
  setSelectedMember(member);
1774
- setSelectedMemberView(null);
1920
+ setSelectedMember(null);
1775
1921
  }
1776
1922
  useStore.getState().closeMemberProfile();
1777
1923
  }, [pendingMemberProfile, membersWithLiveBranches]);
@@ -1837,9 +1983,6 @@ export const TeamDetailView = ({
1837
1983
  void (async () => {
1838
1984
  try {
1839
1985
  const result = await deleteTeam(teamName);
1840
- if (result.restartRequired) {
1841
- await api.ccSettings.restart();
1842
- }
1843
1986
  await fetchTeams();
1844
1987
  setDeleteConfirmOpen(false);
1845
1988
  if (tabId) closeTab(tabId);
@@ -1906,7 +2049,7 @@ export const TeamDetailView = ({
1906
2049
  tasks: data?.tasks ?? [],
1907
2050
  isTeamAlive: data?.isAlive,
1908
2051
  timeWindow,
1909
- teamSessionIds,
2052
+ sessions: ccSessions,
1910
2053
  currentLeadSessionId: data?.config.leadSessionId,
1911
2054
  pendingRepliesByMember,
1912
2055
  onPendingReplyChange: setPendingRepliesByMember,
@@ -1921,12 +2064,12 @@ export const TeamDetailView = ({
1921
2064
  data?.config.leadSessionId,
1922
2065
  data?.isAlive,
1923
2066
  data?.tasks,
2067
+ ccSessions,
1924
2068
  handleReplyToMessage,
1925
2069
  handleRestartTeam,
1926
2070
  handleSelectMember,
1927
2071
  pendingRepliesByMember,
1928
2072
  teamName,
1929
- teamSessionIds,
1930
2073
  timeWindow,
1931
2074
  keepMessagesInline,
1932
2075
  ]
@@ -1989,6 +2132,7 @@ export const TeamDetailView = ({
1989
2132
  if (error === 'TEAM_DRAFT') {
1990
2133
  const draftTeamSummary = useStore.getState().teamByName[teamName];
1991
2134
  const draftDisplayName = draftTeamSummary?.displayName || teamName;
2135
+ const draftCwd = draftTeamSummary?.projectPath || draftTeamSummary?.workDir || '';
1992
2136
 
1993
2137
  return (
1994
2138
  <>
@@ -1996,44 +2140,27 @@ export const TeamDetailView = ({
1996
2140
  <div ref={provisioningBannerRef}>
1997
2141
  <TeamProvisioningBanner teamName={teamName} />
1998
2142
  </div>
1999
- <div className="flex min-h-[calc(100vh-12rem)] items-center justify-center">
2000
- <div className="max-w-md text-center">
2001
- <p className="text-sm font-medium text-text">团队尚未启动</p>
2002
- <p className="mt-2 text-xs text-text-secondary">
2003
- 这是一个草稿团队。<strong>{draftDisplayName}</strong>{' '}
2004
- 尚未完成启动,点击“启动团队”后即可选择模型并进入动态编排。
2005
- </p>
2006
- <div className="mt-4 flex justify-center gap-2">
2007
- <button
2008
- className="rounded-md bg-blue-600 px-4 py-1.5 text-xs font-medium text-white transition-colors hover:bg-blue-500"
2009
- onClick={() => openLaunchDialog('launch')}
2010
- >
2011
- 启动团队
2012
- </button>
2013
- <button
2014
- className="rounded-md bg-surface-raised px-4 py-1.5 text-xs font-medium text-text-secondary transition-colors hover:text-text"
2015
- onClick={() => {
2016
- void api.teams.deleteDraft(teamName).catch(() => {});
2017
- }}
2018
- >
2019
- 删除
2020
- </button>
2021
- </div>
2022
- </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
+ />
2023
2162
  </div>
2024
2163
  </div>
2025
- <LaunchTeamDialog
2026
- mode={launchDialogState.mode}
2027
- open={launchDialogOpen}
2028
- teamName={teamName}
2029
- members={[]}
2030
- defaultProjectPath={draftTeamSummary?.projectPath}
2031
- provisioningError={provisioningError}
2032
- clearProvisioningError={clearProvisioningError}
2033
- onClose={closeLaunchDialog}
2034
- onLaunch={handleLaunchDialogSubmit}
2035
- onRelaunch={handleRelaunchDialogSubmit}
2036
- />
2037
2164
  </>
2038
2165
  );
2039
2166
  }
@@ -2110,13 +2237,16 @@ export const TeamDetailView = ({
2110
2237
  <h2 className="text-base font-semibold text-[var(--color-text)]">
2111
2238
  {data.config.name}
2112
2239
  </h2>
2113
- {data.isAlive && (
2114
- <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
+ >
2115
2245
  <span className="size-1.5 rounded-full bg-emerald-400" />
2116
- 运行中
2246
+ {pl.type}
2117
2247
  </span>
2118
- )}
2119
- {!data.isAlive && isTeamProvisioning && (
2248
+ ))}
2249
+ {isTeamProvisioning && (
2120
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">
2121
2251
  <span className="size-1.5 animate-pulse rounded-full bg-yellow-400" />
2122
2252
  启动中...
@@ -2125,6 +2255,18 @@ export const TeamDetailView = ({
2125
2255
  </div>
2126
2256
  </div>
2127
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}
2128
2270
  <Button
2129
2271
  variant="outline"
2130
2272
  size="sm"
@@ -2146,32 +2288,21 @@ export const TeamDetailView = ({
2146
2288
  </Button>
2147
2289
  </PopoverTrigger>
2148
2290
  <PopoverContent align="end" className="w-44 p-1">
2149
- {data.config.projectPath && (
2150
- <button
2151
- type="button"
2152
- 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)]"
2153
- onClick={() => {
2154
- setHeaderMenuOpen(false);
2155
- setEnvDialogOpen(true);
2156
- }}
2157
- >
2158
- <Shield size={13} />
2159
- 环境变量
2160
- </button>
2161
- )}
2162
- {teamName !== 'default' && teamName !== 'my-project' && (
2163
- <button
2164
- type="button"
2165
- 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"
2166
- onClick={() => {
2167
- setHeaderMenuOpen(false);
2168
- handleDeleteTeam();
2169
- }}
2170
- >
2171
- <Trash2 size={13} />
2172
- 删除团队
2173
- </button>
2174
- )}
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
+ )}
2175
2306
  </PopoverContent>
2176
2307
  </Popover>
2177
2308
  </div>
@@ -2282,15 +2413,38 @@ export const TeamDetailView = ({
2282
2413
  </CollapsibleTeamSection>
2283
2414
 
2284
2415
  <CollapsibleTeamSection
2285
- sectionId="tools"
2286
- title="工具"
2287
- icon={<Wrench size={14} />}
2288
- defaultOpen={false}
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
+ }
2289
2442
  >
2290
- <ToolsSection
2443
+ <CcSessionsSection
2291
2444
  teamName={teamName}
2292
- projectPath={data.config.projectPath ?? null}
2293
- harnessType={data.harness}
2445
+ sessions={ccSessions}
2446
+ loading={ccSessionsLoading}
2447
+ error={ccSessionsError}
2294
2448
  />
2295
2449
  </CollapsibleTeamSection>
2296
2450
 
@@ -2487,8 +2641,6 @@ export const TeamDetailView = ({
2487
2641
  </div>
2488
2642
  </CollapsibleTeamSection>
2489
2643
 
2490
- <TeamMessagesPanelBridge position="inline" {...sharedMessagesPanelProps} />
2491
-
2492
2644
  {(data.processes?.length ?? 0) > 0 && (
2493
2645
  <CollapsibleTeamSection
2494
2646
  sectionId="processes"
@@ -2547,8 +2699,6 @@ export const TeamDetailView = ({
2547
2699
  teamName={teamName}
2548
2700
  members={membersWithLiveBranches}
2549
2701
  tasks={data.tasks}
2550
- initialTab={selectedMemberView?.initialTab}
2551
- initialActivityFilter={selectedMemberView?.initialActivityFilter}
2552
2702
  isTeamAlive={data.isAlive}
2553
2703
  isTeamProvisioning={isTeamProvisioning}
2554
2704
  launchParams={launchParams}
@@ -2595,18 +2745,6 @@ export const TeamDetailView = ({
2595
2745
  }}
2596
2746
  />
2597
2747
 
2598
- <Dialog open={envDialogOpen} onOpenChange={setEnvDialogOpen}>
2599
- <DialogContent className="max-h-[80vh] max-w-lg overflow-y-auto">
2600
- <DialogHeader>
2601
- <DialogTitle>项目环境变量</DialogTitle>
2602
- <DialogDescription>
2603
- 管理当前项目所需的环境变量,供 MCP 和 Skills 使用。
2604
- </DialogDescription>
2605
- </DialogHeader>
2606
- <ProjectEnvPanel projectPath={data.config.projectPath ?? null} />
2607
- </DialogContent>
2608
- </Dialog>
2609
-
2610
2748
  <Dialog
2611
2749
  open={removeMemberConfirm !== null}
2612
2750
  onOpenChange={(open) => {
@@ -2785,14 +2923,72 @@ export const TeamDetailView = ({
2785
2923
  {spawnStatusWatcher}
2786
2924
  {teamAgentRuntimeWatcher}
2787
2925
  {leadContextWatcher}
2788
- {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}
2789
2988
  <EditTeamDialog
2790
2989
  open={editDialogOpen}
2791
2990
  teamName={teamName}
2792
2991
  onClose={() => setEditDialogOpen(false)}
2793
- onDeleteTeam={
2794
- teamName !== 'default' && teamName !== 'my-project' ? handleDeleteTeam : undefined
2795
- }
2796
2992
  />
2797
2993
  </>
2798
2994
  );