diff --git a/src/renderer/src/components/activity/activity-event-build-cache.ts b/src/renderer/src/components/activity/activity-event-build-cache.ts index 1dcaf94d2aa..346390d5619 100644 --- a/src/renderer/src/components/activity/activity-event-build-cache.ts +++ b/src/renderer/src/components/activity/activity-event-build-cache.ts @@ -87,21 +87,19 @@ export function resolvePaneBuild( return { events: cached.events, live: cached.live } } - const events = - inputsUnchanged && liveMatchesCache - ? cached.events - : buildPaneActivityEvents({ - entry: rowEntry, - worktree: request.worktree, - repo: request.repo, - tab: request.tab, - agentType: request.agentType, - agentAlive: request.agentAlive, - acknowledgedAt: request.acknowledgedAt, - clearedAt: request.clearedAt, - liveState: request.liveState, - migrationUnsupportedPtyId: request.migrationUnsupportedPtyId - }) + // The live turn is itself an event, so a live change always rebuilds the pane's events. + const events = buildPaneActivityEvents({ + entry: rowEntry, + worktree: request.worktree, + repo: request.repo, + tab: request.tab, + agentType: request.agentType, + agentAlive: request.agentAlive, + acknowledgedAt: request.acknowledgedAt, + clearedAt: request.clearedAt, + liveState: request.liveState, + migrationUnsupportedPtyId: request.migrationUnsupportedPtyId + }) const live: ActivityLiveAgentSnapshot | null = request.liveState === null ? null diff --git a/src/renderer/src/components/activity/useActivityUnreadCount.test.ts b/src/renderer/src/components/activity/useActivityUnreadCount.test.ts index a31fae78f89..b2ab75e9564 100644 --- a/src/renderer/src/components/activity/useActivityUnreadCount.test.ts +++ b/src/renderer/src/components/activity/useActivityUnreadCount.test.ts @@ -114,13 +114,14 @@ describe('countActivityUnread source overlap', () => { }) describe('countActivityUnread working turns', () => { - it('counts fresh working and monitoring, but not historical or retained working', () => { + it('counts fresh working, but not monitoring, historical, or retained working', () => { const entry = makeEntry({ state: 'working', stateHistory: [{ state: 'working', prompt: 'old', startedAt: 1_000 }] }) expect(countActivityUnread(makeSource(entry), 2_000)).toBe(1) - expect(countActivityUnread(makeSource({ ...entry, workingMode: 'monitoring' }), 2_000)).toBe(1) + // Monitoring emits no unread event in the list (4b2e3dded0), so the badge must not count it. + expect(countActivityUnread(makeSource({ ...entry, workingMode: 'monitoring' }), 2_000)).toBe(0) expect( countActivityUnread( { diff --git a/src/renderer/src/components/activity/useActivityUnreadCount.ts b/src/renderer/src/components/activity/useActivityUnreadCount.ts index 77a98b3f448..e727ef969e2 100644 --- a/src/renderer/src/components/activity/useActivityUnreadCount.ts +++ b/src/renderer/src/components/activity/useActivityUnreadCount.ts @@ -39,9 +39,11 @@ export function countActivityUnread(source: ActivityUnreadCountSource, now = Dat } } // Why: a session-boundary done is an idle connect (STA-3386), not an event to read. + // Why 'working' only: a monitoring turn surfaces through the live snapshot, never as an + // unread event, so counting it here would light the badge with no unread row to clear. if ( (isHistoricalActivityState(entry.state) || - (live && freshActivityLiveAgentState(entry, now) !== null)) && + (live && freshActivityLiveAgentState(entry, now) === 'working')) && entry.sessionBoundary !== true && mutedAt < entry.stateStartedAt ) {