@@ -403,16 +442,13 @@ function ToolCallGroupMulti({ toolCalls, resultMap, childToolCallsByParent, isSt
{summary}
- {!isStreaming && allComplete && !errorPresent && (
+ {!isRunning && !errorPresent && (
check_circle
)}
- {!isStreaming && errorPresent && (
+ {!isRunning && errorPresent && (
error
)}
- {!isStreaming && !allComplete && !errorPresent && (
-
pending
- )}
- {isStreaming && (
+ {isRunning && (
)}
diff --git a/desktop/src/pages/ActiveSession.test.tsx b/desktop/src/pages/ActiveSession.test.tsx
index 446225e4..ca5428cc 100644
--- a/desktop/src/pages/ActiveSession.test.tsx
+++ b/desktop/src/pages/ActiveSession.test.tsx
@@ -277,6 +277,68 @@ describe('ActiveSession task polling', () => {
expect(screen.getByTestId('message-list')).toBeInTheDocument()
})
+ it('keeps the session header active while a background task is still running after the turn completes', () => {
+ const sessionId = 'background-shell-running-session'
+
+ useSessionStore.setState({
+ sessions: [{
+ id: sessionId,
+ title: 'Background Shell Session',
+ createdAt: '2026-05-07T00:00:00.000Z',
+ modifiedAt: new Date().toISOString(),
+ messageCount: 1,
+ projectPath: '/workspace/project',
+ workDir: '/workspace/project',
+ workDirExists: true,
+ }],
+ activeSessionId: sessionId,
+ isLoading: false,
+ error: null,
+ })
+ useTabStore.setState({
+ tabs: [{ sessionId, title: 'Background Shell Session', type: 'session', status: 'idle' }],
+ activeTabId: sessionId,
+ })
+ useChatStore.setState({
+ sessions: {
+ [sessionId]: {
+ messages: [{ id: 'msg-1', type: 'assistant_text', content: 'task started', timestamp: 1 }],
+ backgroundAgentTasks: {
+ 'bash-task-1': {
+ taskId: 'bash-task-1',
+ toolUseId: 'bash-tool-1',
+ status: 'running',
+ taskType: 'local_bash',
+ description: 'Run page integration checks',
+ startedAt: 1,
+ updatedAt: 2,
+ },
+ },
+ chatState: 'idle',
+ connectionState: 'connected',
+ streamingText: '',
+ streamingToolInput: '',
+ activeToolUseId: null,
+ activeToolName: null,
+ activeThinkingId: null,
+ pendingPermission: null,
+ pendingComputerUsePermission: null,
+ tokenUsage: { input_tokens: 0, output_tokens: 0 },
+ elapsedSeconds: 0,
+ statusVerb: '',
+ slashCommands: [],
+ agentTaskNotifications: {},
+ elapsedTimer: null,
+ },
+ },
+ })
+
+ render(
)
+
+ expect(screen.getByText(/session active|会话活跃中/)).toBeInTheDocument()
+ expect(screen.getByTestId('chat-input')).toHaveAttribute('data-variant', 'default')
+ })
+
it('refreshes CLI tasks repeatedly while a turn is active', async () => {
vi.useFakeTimers()
diff --git a/desktop/src/pages/ActiveSession.tsx b/desktop/src/pages/ActiveSession.tsx
index f323533c..a4a7a0fb 100644
--- a/desktop/src/pages/ActiveSession.tsx
+++ b/desktop/src/pages/ActiveSession.tsx
@@ -210,8 +210,11 @@ export function ActiveSession() {
const fetchSessionTasks = useCLITaskStore((s) => s.fetchSessionTasks)
const trackedTaskSessionId = useCLITaskStore((s) => s.sessionId)
const hasIncompleteTasks = useCLITaskStore((s) => s.tasks.some((task) => task.status !== 'completed'))
+ const hasRunningTasks = useCLITaskStore((s) => s.tasks.some((task) => task.status === 'in_progress'))
const chatState = sessionState?.chatState ?? 'idle'
const tokenUsage = sessionState?.tokenUsage ?? { input_tokens: 0, output_tokens: 0 }
+ const hasRunningBackgroundTasks = Object.values(sessionState?.backgroundAgentTasks ?? {})
+ .some((task) => task.status === 'running')
const session = sessions.find((s) => s.id === activeTabId)
const memberInfo = useTeamStore((s) => activeTabId ? s.getMemberBySessionId(activeTabId) : null)
@@ -265,7 +268,9 @@ export function ActiveSession() {
const streamingText = sessionState?.streamingText ?? ''
const isEmpty = messages.length === 0 && !streamingText && (session?.messageCount ?? 0) === 0
- const isActive = chatState !== 'idle'
+ const isActive = chatState !== 'idle' ||
+ (trackedTaskSessionId === activeTabId && hasRunningTasks) ||
+ hasRunningBackgroundTasks
const totalTokens = tokenUsage.input_tokens + tokenUsage.output_tokens
const lastUpdated = useMemo(() => {