diff --git a/packages/types/src/vscode-extension-host.ts b/packages/types/src/vscode-extension-host.ts index 7ae89e8777..d1435500da 100644 --- a/packages/types/src/vscode-extension-host.ts +++ b/packages/types/src/vscode-extension-host.ts @@ -385,6 +385,7 @@ export type ExtensionState = Pick< organizationSettingsVersion?: number isBrowserSessionActive: boolean // Actual browser session state + activeTerminalSessions: number // Count of active terminal sessions for current task autoCondenseContext: boolean autoCondenseContextPercent: number diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index b101bee7d2..00a29930c3 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -62,6 +62,7 @@ import { EMBEDDING_MODEL_PROFILES } from "../../shared/embeddingModels" import { ProfileValidator } from "../../shared/ProfileValidator" import { Terminal } from "../../integrations/terminal/Terminal" +import { ProcessManager } from "../../integrations/terminal/ProcessManager" import { downloadTask, getTaskFileName } from "../../integrations/misc/export-markdown" import { resolveDefaultSaveUri, saveLastExportPath } from "../../utils/export" import { getTheme } from "../../integrations/theme/getTheme" @@ -2074,6 +2075,7 @@ export class ClineProvider openRouterImageGenerationSelectedModel, featureRoomoteControlEnabled, isBrowserSessionActive, + activeTerminalSessions, } = await this.getState() let cloudOrganizations: CloudOrganizationMembership[] = [] @@ -2123,6 +2125,7 @@ export class ClineProvider alwaysAllowModeSwitch: alwaysAllowModeSwitch ?? false, alwaysAllowSubtasks: alwaysAllowSubtasks ?? false, isBrowserSessionActive, + activeTerminalSessions, allowedMaxRequests, allowedMaxCost, autoCondenseContext: autoCondenseContext ?? true, @@ -2357,6 +2360,14 @@ export class ClineProvider // Get actual browser session state const isBrowserSessionActive = this.getCurrentTask()?.browserSession?.isSessionActive() ?? false + // Get active terminal sessions count for current task + const currentTaskId = this.getCurrentTask()?.taskId + const activeTerminalSessions = currentTaskId + ? ProcessManager.getInstance() + .listSessions(currentTaskId) + .filter((s) => s.running).length + : 0 + // Return the same structure as before. return { apiConfiguration: providerSettings, @@ -2375,6 +2386,7 @@ export class ClineProvider alwaysAllowSubtasks: stateValues.alwaysAllowSubtasks ?? false, alwaysAllowFollowupQuestions: stateValues.alwaysAllowFollowupQuestions ?? false, isBrowserSessionActive, + activeTerminalSessions, followupAutoApproveTimeoutMs: stateValues.followupAutoApproveTimeoutMs ?? 60000, diagnosticsEnabled: stateValues.diagnosticsEnabled ?? true, allowedMaxRequests: stateValues.allowedMaxRequests, diff --git a/webview-ui/src/components/chat/TaskHeader.tsx b/webview-ui/src/components/chat/TaskHeader.tsx index d5424b7422..09e7e070c3 100644 --- a/webview-ui/src/components/chat/TaskHeader.tsx +++ b/webview-ui/src/components/chat/TaskHeader.tsx @@ -11,6 +11,7 @@ import { FoldVertical, Globe, ArrowLeft, + TerminalSquare, } from "lucide-react" import prettyBytes from "pretty-bytes" @@ -68,7 +69,8 @@ const TaskHeader = ({ todos, }: TaskHeaderProps) => { const { t } = useTranslation() - const { apiConfiguration, currentTaskItem, clineMessages, isBrowserSessionActive } = useExtensionState() + const { apiConfiguration, currentTaskItem, clineMessages, isBrowserSessionActive, activeTerminalSessions } = + useExtensionState() const { id: modelId, info: model } = useSelectedModel(apiConfiguration) const [isTaskExpanded, setIsTaskExpanded] = useState(false) const [showLongRunningTaskMessage, setShowLongRunningTaskMessage] = useState(false) @@ -369,6 +371,30 @@ const TaskHeader = ({ )} )} + {(activeTerminalSessions ?? 0) > 0 && ( +