✦
{verb}...
- {elapsedSeconds > 0 && (
-
- {formatElapsed(elapsedSeconds)}
-
- )}
+ {turnStartedAt !== undefined &&
}
{tokenUsage.output_tokens > 0 && (
· ↓ {tokenUsage.output_tokens}
diff --git a/desktop/src/components/chat/chatBlocks.test.tsx b/desktop/src/components/chat/chatBlocks.test.tsx
index 83add1f8..a4d5dfae 100644
--- a/desktop/src/components/chat/chatBlocks.test.tsx
+++ b/desktop/src/components/chat/chatBlocks.test.tsx
@@ -214,11 +214,9 @@ describe('chat blocks', () => {
},
},
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
diff --git a/desktop/src/components/workspace/WorkspacePanel.test.tsx b/desktop/src/components/workspace/WorkspacePanel.test.tsx
index 7d0772c1..6ac9be58 100644
--- a/desktop/src/components/workspace/WorkspacePanel.test.tsx
+++ b/desktop/src/components/workspace/WorkspacePanel.test.tsx
@@ -1255,11 +1255,9 @@ describe('WorkspacePanel', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
diff --git a/desktop/src/pages/ActiveSession.test.tsx b/desktop/src/pages/ActiveSession.test.tsx
index 7db6f21c..a3252cd6 100644
--- a/desktop/src/pages/ActiveSession.test.tsx
+++ b/desktop/src/pages/ActiveSession.test.tsx
@@ -126,11 +126,9 @@ describe('ActiveSession task polling', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -193,11 +191,9 @@ describe('ActiveSession task polling', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -260,11 +256,9 @@ describe('ActiveSession task polling', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -333,11 +327,9 @@ describe('ActiveSession task polling', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -394,11 +386,9 @@ describe('ActiveSession task polling', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -454,11 +444,9 @@ describe('ActiveSession task polling', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -532,11 +520,9 @@ describe('ActiveSession task polling', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -586,11 +572,9 @@ describe('ActiveSession task polling', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -676,11 +660,9 @@ describe('ActiveSession task polling', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -730,11 +712,9 @@ describe('ActiveSession task polling', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -782,11 +762,9 @@ describe('ActiveSession task polling', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -839,11 +817,9 @@ describe('ActiveSession task polling', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
diff --git a/desktop/src/stores/chatStore.test.ts b/desktop/src/stores/chatStore.test.ts
index d5a740b4..f9b48c2c 100644
--- a/desktop/src/stores/chatStore.test.ts
+++ b/desktop/src/stores/chatStore.test.ts
@@ -155,13 +155,11 @@ function makeSession(overrides: Partial = {}): PerSessionState
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
apiRetry: null,
slashCommands: [],
agentTaskNotifications: {},
backgroundAgentTasks: {},
- elapsedTimer: null,
...overrides,
}
}
@@ -623,11 +621,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -707,11 +703,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -820,11 +814,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [{ name: 'old-command', description: 'Old command' }],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -1234,11 +1226,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -1298,11 +1288,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -1328,11 +1316,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -1622,11 +1608,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: 'thinking-1',
pendingPermission: null,
tokenUsage: { input_tokens: 12, output_tokens: 34 },
- elapsedSeconds: 5,
statusVerb: 'Thinking',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -1694,11 +1678,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -1736,11 +1718,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: 'Compacting conversation',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -1787,11 +1767,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -1831,11 +1809,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -1881,11 +1857,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -2112,11 +2086,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -2156,11 +2128,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -2264,11 +2234,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -2553,11 +2521,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -2598,6 +2564,21 @@ describe('chatStore history mapping', () => {
vi.useRealTimers()
})
+ it('leaves the store unchanged while a turn runs', () => {
+ vi.useFakeTimers()
+ useChatStore.setState({ sessions: { [TEST_SESSION_ID]: makeSession({ chatState: 'idle' }) } })
+ sendSnapshot(mirrorState())
+
+ useChatStore.getState().sendMessage(TEST_SESSION_ID, 'hello')
+ sendPatch(2, { turn: { phase: 'running', startedAt: Date.now(), activity: 'thinking' } })
+ const sessions = useChatStore.getState().sessions
+
+ vi.advanceTimersByTime(5000)
+
+ expect(useChatStore.getState().sessions).toBe(sessions)
+ vi.useRealTimers()
+ })
+
it('shows worktree creation while the CLI starts and clears it once it runs', () => {
useChatStore.setState({ sessions: { [TEST_SESSION_ID]: makeSession({ chatState: 'idle' }) } })
@@ -2661,11 +2642,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -2712,11 +2691,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -2743,11 +2720,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -2781,11 +2756,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
@@ -2821,11 +2794,9 @@ describe('chatStore history mapping', () => {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
},
},
})
diff --git a/desktop/src/stores/chatStore.ts b/desktop/src/stores/chatStore.ts
index 297b1997..ffc44088 100644
--- a/desktop/src/stores/chatStore.ts
+++ b/desktop/src/stores/chatStore.ts
@@ -67,14 +67,12 @@ export type PerSessionState = {
description?: string
} | null
tokenUsage: TokenUsage
- elapsedSeconds: number
statusVerb: string
apiRetry?: ApiRetryState | null
slashCommands: Array<{ name: string; description: string; argumentHint?: string }>
agentTaskNotifications: Record
backgroundAgentTasks?: Record
activeGoal?: ActiveGoalState | null
- elapsedTimer: ReturnType | null
composerPrefill?: {
text: string
attachments?: UIAttachment[]
@@ -97,14 +95,12 @@ const DEFAULT_SESSION_STATE: PerSessionState = {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
apiRetry: null,
slashCommands: [],
agentTaskNotifications: {},
backgroundAgentTasks: {},
activeGoal: null,
- elapsedTimer: null,
composerPrefill: null,
composerInsertion: null,
composerDraft: null,
@@ -763,8 +759,6 @@ export const useChatStore = create((set, get) => ({
prewarmPending.delete(sessionId)
reconnectingSessions.delete(sessionId)
reloadAtTurnEnd.delete(sessionId)
- const session = get().sessions[sessionId]
- if (session?.elapsedTimer) clearInterval(session.elapsedTimer)
if (pendingDeltaBySession.has(sessionId)) {
const text = consumePendingDelta(sessionId)
set((s) => ({ sessions: updateSessionIn(s.sessions, sessionId, (sess) => ({ streamingText: sess.streamingText + text })) }))
@@ -843,14 +837,6 @@ export const useChatStore = create((set, get) => ({
...(isMemberSession ? { pending: true } : {}),
})
- if (!isMemberSession && session.elapsedTimer) clearInterval(session.elapsedTimer)
-
- const timer = !isMemberSession
- ? setInterval(() => {
- set((st) => ({ sessions: updateSessionIn(st.sessions, sessionId, (sess) => ({ elapsedSeconds: sess.elapsedSeconds + 1 })) }))
- }, 1000)
- : null
-
return {
sessions: {
...s.sessions,
@@ -858,11 +844,9 @@ export const useChatStore = create((set, get) => ({
...session,
messages: newMessages,
chatState: 'thinking',
- elapsedSeconds: 0,
streamingText: '',
statusVerb: isMemberSession ? '' : randomSpinnerVerb(),
apiRetry: null,
- elapsedTimer: timer,
connectionState: isMemberSession ? 'connected' : session.connectionState,
},
},
@@ -932,7 +916,6 @@ export const useChatStore = create((set, get) => ({
set((s) => {
const session = s.sessions[sessionId]
if (!session) return s
- if (session.elapsedTimer) clearInterval(session.elapsedTimer)
return {
sessions: {
...s.sessions,
@@ -941,7 +924,6 @@ export const useChatStore = create((set, get) => ({
chatState: 'idle',
pendingPermission: null,
apiRetry: null,
- elapsedTimer: null,
},
},
}
@@ -1014,7 +996,6 @@ export const useChatStore = create((set, get) => ({
set((state) => {
const session = state.sessions[sessionId]
if (!session) return state
- if (session.elapsedTimer) clearInterval(session.elapsedTimer)
return {
sessions: updateSessionIn(state.sessions, sessionId, () => ({
messages: mergeBackgroundTaskMessages(uiMessages, restoredBackgroundTasks),
@@ -1028,7 +1009,6 @@ export const useChatStore = create((set, get) => ({
streamingText: '',
streamingToolInput: '',
pendingPermission: null,
- elapsedTimer: null,
statusVerb: '',
apiRetry: null,
})),
@@ -1414,13 +1394,11 @@ export const useChatStore = create((set, get) => ({
} else if (text !== session.streamingText) {
update(() => ({ streamingText: text }))
}
- if (session.elapsedTimer) clearInterval(session.elapsedTimer)
update(() => ({
tokenUsage: msg.usage,
chatState: 'idle',
activeThinkingId: null,
pendingPermission: null,
- elapsedTimer: null,
apiRetry: null,
}))
useTabStore.getState().updateTabStatus(sessionId, 'idle')
@@ -1461,13 +1439,6 @@ export const useChatStore = create((set, get) => ({
}
})
useTabStore.getState().updateTabStatus(sessionId, 'error')
- {
- const session = get().sessions[sessionId]
- if (session?.elapsedTimer) {
- clearInterval(session.elapsedTimer)
- update(() => ({ elapsedTimer: null }))
- }
- }
break
case 'team_created':
@@ -1500,8 +1471,6 @@ export const useChatStore = create((set, get) => ({
})
}
if (msg.subtype === 'session_cleared') {
- const session = get().sessions[sessionId]
- if (session?.elapsedTimer) clearInterval(session.elapsedTimer)
update(() => ({
messages: [],
streamingText: '',
@@ -1511,8 +1480,6 @@ export const useChatStore = create((set, get) => ({
activeThinkingId: null,
pendingPermission: null,
chatState: 'idle',
- elapsedTimer: null,
- elapsedSeconds: 0,
statusVerb: '',
apiRetry: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
@@ -1801,8 +1768,6 @@ function applyMirror(sessionId: string, next: SessionState): void {
messages = dropTailCompactingCompactSummary(messages)
}
- let elapsedTimer = current.elapsedTimer
- let elapsedSeconds = current.elapsedSeconds
if (!isRunning) {
const pendingText = `${streamingText}${consumePendingDelta(sessionId)}`
if (wasRunning && pendingText.trim()) {
@@ -1811,15 +1776,6 @@ function applyMirror(sessionId: string, next: SessionState): void {
} else {
streamingText = pendingText
}
- if (elapsedTimer) clearInterval(elapsedTimer)
- elapsedTimer = null
- } else if (!elapsedTimer) {
- elapsedSeconds = next.turn.startedAt ? Math.max(0, Math.floor((now - next.turn.startedAt) / 1000)) : 0
- elapsedTimer = setInterval(() => {
- useChatStore.setState((st) => ({
- sessions: updateSessionIn(st.sessions, sessionId, (sess) => ({ elapsedSeconds: sess.elapsedSeconds + 1 })),
- }))
- }, 1000)
}
const previousVerb = specialStatusVerb(previous)
@@ -1836,8 +1792,6 @@ function applyMirror(sessionId: string, next: SessionState): void {
permissionMode: displayedPermissionMode(next),
pendingPermission: pendingPermissionFromMirror(next),
chatState: chatStateFromMirror(next),
- elapsedTimer,
- elapsedSeconds,
...(nextVerb ? { statusVerb: nextVerb } : previousVerb ? { statusVerb: '' } : {}),
...(!isRunning ? { activeThinkingId: null, apiRetry: null, statusVerb: nextVerb } : {}),
},
diff --git a/desktop/src/stores/teamStore.ts b/desktop/src/stores/teamStore.ts
index 7c30d7d9..81e239ee 100644
--- a/desktop/src/stores/teamStore.ts
+++ b/desktop/src/stores/teamStore.ts
@@ -26,11 +26,9 @@ function createMemberSessionState() {
activeThinkingId: null,
pendingPermission: null,
tokenUsage: { input_tokens: 0, output_tokens: 0 },
- elapsedSeconds: 0,
statusVerb: '',
slashCommands: [],
agentTaskNotifications: {},
- elapsedTimer: null,
}
}