perf(renderer): gate agent completion coordinator sync (#8149)

This commit is contained in:
Neil
2026-07-10 20:52:13 -07:00
committed by GitHub
parent a4fb828b3c
commit 141b9b7de2
6 changed files with 363 additions and 10 deletions
@@ -824,6 +824,37 @@ describe('agent hook completion notifications', () => {
expect(_getAgentHookCompletionNotificationCoordinatorCountForTest()).toBe(3)
})
it('gates cosmetic store updates but still prunes after a pane closes', async () => {
const {
_getAgentHookCompletionNotificationCoordinatorCountForTest,
observeAgentHookCompletionForNotification,
syncAgentHookCompletionNotificationsForStoreUpdate
} = await import('./agent-hook-completion-notifications')
observeAgentHookCompletionForNotification({
paneKey,
worktreeId: 'wt-1',
payload: hookStatus('working')
})
const beforeCosmeticUpdate = { ...mockStoreState }
mockStoreState.tabsByWorktree = {
'wt-1': [{ id: 'tab-1', ptyId: 'pty-1' }]
}
expect(
syncAgentHookCompletionNotificationsForStoreUpdate(mockStoreState, beforeCosmeticUpdate)
).toBe(false)
expect(_getAgentHookCompletionNotificationCoordinatorCountForTest()).toBe(1)
const beforeClose = { ...mockStoreState }
mockStoreState.tabsByWorktree = { 'wt-1': [] }
mockStoreState.ptyIdsByTabId = {}
expect(syncAgentHookCompletionNotificationsForStoreUpdate(mockStoreState, beforeClose)).toBe(
true
)
expect(_getAgentHookCompletionNotificationCoordinatorCountForTest()).toBe(0)
})
it('skips tab scans until a pane-liveness slice changes', async () => {
seedManyLivePanes()
const {
@@ -10,6 +10,10 @@ import { dispatchTerminalNotification } from '@/components/terminal-pane/use-not
import { collectLeafIdsInOrder } from '@/components/terminal-pane/layout-serialization'
import { createCodexAutoApprovalHookCompletionSuppressor } from '@/components/terminal-pane/codex-auto-approval-notification-suppression'
import { dispatchAgentHookTerminalLifecycle } from '@/components/terminal-pane/agent-hook-terminal-lifecycle'
import {
shouldSyncAgentHookCompletionForStoreUpdate,
type AgentHookCompletionStoreSnapshot
} from './agent-hook-completion-store-sync'
type CoordinatorEntry = {
worktreeId: string
@@ -19,7 +23,8 @@ type CoordinatorEntry = {
type StoreSnapshot = ReturnType<typeof useAppStore.getState>
type WorktreeTab = NonNullable<StoreSnapshot['tabsByWorktree']>[string][number]
// Why: a paneKey resolves to a tab by id. Prebuilding this index once per prune
// pass avoids re-flattening tabsByWorktree per coordinator (O(coordinators x tabs)).
// pass avoids re-flattening tabsByWorktree per coordinator (O(coordinators x
// tabs)) when a liveness or notification-setting update requires a prune.
type TabIndex = ReadonlyMap<string, WorktreeTab>
type PaneCoordinatorLivenessSnapshot = Pick<
StoreSnapshot,
@@ -119,6 +124,19 @@ export function syncAgentHookCompletionNotificationSettings(): boolean {
return enabled
}
export function syncAgentHookCompletionNotificationsForStoreUpdate(
current: AgentHookCompletionStoreSnapshot,
previous: AgentHookCompletionStoreSnapshot
): boolean {
// Why: Zustand also publishes high-rate title/status writes that cannot make
// module-scoped completion coordinators stale.
if (!shouldSyncAgentHookCompletionForStoreUpdate(current, previous)) {
return false
}
syncAgentHookCompletionNotificationSettings()
return true
}
function getPtyIdForPaneKey(paneKey: string): string | null {
const parsed = parsePaneKey(paneKey)
if (!parsed) {
@@ -0,0 +1,190 @@
import { describe, expect, it } from 'vitest'
import {
_measureAgentHookCompletionStoreSyncForTest,
shouldSyncAgentHookCompletionForStoreUpdate,
type AgentHookCompletionStoreSnapshot
} from './agent-hook-completion-store-sync'
function createState(
overrides: Partial<AgentHookCompletionStoreSnapshot> = {}
): AgentHookCompletionStoreSnapshot {
return {
settings: {
experimentalTerminalAttention: false,
notifications: { enabled: true, agentTaskComplete: true }
},
tabsByWorktree: {
'wt-1': [{ id: 'tab-1', ptyId: 'pty-1', title: 'Terminal 1' }]
},
ptyIdsByTabId: { 'tab-1': ['pty-1'] },
terminalLayoutsByTabId: {},
suppressedPtyExitIds: {},
...overrides
}
}
describe('agent hook completion store sync', () => {
it('ignores unrelated and title-only writes at accumulated-workspace scale', () => {
const tabCount = 100
const coordinatorCount = 100
const updateCount = 600
const tabsByWorktree = Object.fromEntries(
Array.from({ length: tabCount }, (_, index) => [
`wt-${index}`,
[{ id: `tab-${index}`, ptyId: `pty-${index}`, title: `Terminal ${index}` }]
])
)
let previous = createState({ tabsByWorktree })
let syncPasses = 0
let tabVisits = 0
for (let index = 0; index < updateCount; index += 1) {
const current = { ...previous }
const measurement = _measureAgentHookCompletionStoreSyncForTest(current, previous)
syncPasses += Number(measurement.shouldSync)
tabVisits += measurement.tabVisits
previous = current
}
for (let index = 0; index < updateCount; index += 1) {
const targetTabs = previous.tabsByWorktree['wt-99']
if (!targetTabs) {
throw new Error('Expected title-update fixture tab')
}
const current = createState({
...previous,
tabsByWorktree: {
...previous.tabsByWorktree,
'wt-99': targetTabs.map((tab) => ({ ...tab, title: `Agent frame ${index}` }))
}
})
const measurement = _measureAgentHookCompletionStoreSyncForTest(current, previous)
syncPasses += Number(measurement.shouldSync)
tabVisits += measurement.tabVisits
previous = current
}
const previousFullPassCost = {
tabVisits: updateCount * 2 * tabCount,
coordinatorVisits: updateCount * 2 * coordinatorCount
}
const gatedCost = {
tabVisits,
coordinatorVisits: syncPasses * coordinatorCount
}
expect(previousFullPassCost).toEqual({ tabVisits: 120_000, coordinatorVisits: 120_000 })
expect(gatedCost).toEqual({ tabVisits: 600, coordinatorVisits: 0 })
})
it('keeps every coordinator-liveness input reactive', () => {
const previous = createState()
const titleOnly = createState({
...previous,
tabsByWorktree: {
'wt-1': [{ id: 'tab-1', ptyId: 'pty-1', title: 'Codex working' }]
}
})
expect(shouldSyncAgentHookCompletionForStoreUpdate(titleOnly, previous)).toBe(false)
const tabCreated = createState({
...previous,
tabsByWorktree: {
...previous.tabsByWorktree,
'wt-2': [{ id: 'tab-2', ptyId: null }]
}
})
expect(shouldSyncAgentHookCompletionForStoreUpdate(tabCreated, previous)).toBe(true)
const tabRemoved = createState({ ...previous, tabsByWorktree: {} })
expect(shouldSyncAgentHookCompletionForStoreUpdate(tabRemoved, previous)).toBe(true)
const tabPtyChanged = createState({
...previous,
tabsByWorktree: { 'wt-1': [{ id: 'tab-1', ptyId: 'pty-2' }] }
})
expect(shouldSyncAgentHookCompletionForStoreUpdate(tabPtyChanged, previous)).toBe(true)
expect(
shouldSyncAgentHookCompletionForStoreUpdate(
createState({ ...previous, ptyIdsByTabId: { 'tab-1': ['pty-2'] } }),
previous
)
).toBe(true)
expect(
shouldSyncAgentHookCompletionForStoreUpdate(
createState({ ...previous, terminalLayoutsByTabId: { 'tab-1': { root: null } } }),
previous
)
).toBe(true)
expect(
shouldSyncAgentHookCompletionForStoreUpdate(
createState({ ...previous, suppressedPtyExitIds: { 'pty-1': true } }),
previous
)
).toBe(true)
const trackingDisabled = createState({
...previous,
settings: {
experimentalTerminalAttention: false,
notifications: { enabled: false, agentTaskComplete: false }
}
})
expect(shouldSyncAgentHookCompletionForStoreUpdate(trackingDisabled, previous)).toBe(true)
})
it('treats tab order and duplicate-id worktree precedence as liveness inputs', () => {
const previous = createState({
tabsByWorktree: {
'wt-1': [{ id: 'tab-shared', ptyId: 'pty-first' }],
'wt-2': [{ id: 'tab-shared', ptyId: 'pty-second' }]
}
})
const reorderedWorktrees = createState({
...previous,
tabsByWorktree: {
'wt-2': previous.tabsByWorktree['wt-2'] ?? [],
'wt-1': previous.tabsByWorktree['wt-1'] ?? []
}
})
expect(shouldSyncAgentHookCompletionForStoreUpdate(reorderedWorktrees, previous)).toBe(true)
const twoTabPrevious = createState({
tabsByWorktree: {
'wt-1': [
{ id: 'tab-1', ptyId: 'pty-1' },
{ id: 'tab-2', ptyId: 'pty-2' }
]
}
})
const reorderedTabs = createState({
...twoTabPrevious,
tabsByWorktree: {
'wt-1': [
{ id: 'tab-2', ptyId: 'pty-2' },
{ id: 'tab-1', ptyId: 'pty-1' }
]
}
})
expect(shouldSyncAgentHookCompletionForStoreUpdate(reorderedTabs, twoTabPrevious)).toBe(true)
})
it('compares effective tracking state instead of unrelated settings identity', () => {
const previous = createState({
settings: {
experimentalTerminalAttention: true,
notifications: { enabled: false, agentTaskComplete: false }
}
})
const stillTrackedByNotifications = createState({
...previous,
settings: {
experimentalTerminalAttention: false,
notifications: { enabled: true, agentTaskComplete: true }
}
})
expect(shouldSyncAgentHookCompletionForStoreUpdate(stillTrackedByNotifications, previous)).toBe(
false
)
})
})
@@ -0,0 +1,112 @@
type CompletionNotificationSettings = {
readonly enabled?: boolean
readonly agentTaskComplete?: boolean
}
type CompletionStoreSettings = {
readonly notifications?: CompletionNotificationSettings
readonly experimentalTerminalAttention?: boolean
}
type CompletionTerminalTab = {
readonly id: string
readonly ptyId?: string | null
readonly title?: string
}
export type AgentHookCompletionStoreSnapshot = {
readonly settings: CompletionStoreSettings | null
readonly tabsByWorktree: Readonly<Record<string, readonly CompletionTerminalTab[]>>
readonly ptyIdsByTabId: Readonly<Record<string, readonly string[]>>
readonly terminalLayoutsByTabId: Readonly<Record<string, unknown>>
readonly suppressedPtyExitIds: Readonly<Record<string, boolean>>
}
type TabVisit = () => void
function isTrackingEnabled(state: AgentHookCompletionStoreSnapshot): boolean {
const notifications = state.settings?.notifications
const notificationEnabled =
notifications?.enabled !== false && notifications?.agentTaskComplete !== false
return notificationEnabled || state.settings?.experimentalTerminalAttention === true
}
function terminalTabLivenessMatches(
current: AgentHookCompletionStoreSnapshot['tabsByWorktree'],
previous: AgentHookCompletionStoreSnapshot['tabsByWorktree'],
visitTab?: TabVisit
): boolean {
if (current === previous) {
return true
}
const currentWorktreeIds = Object.keys(current)
const previousWorktreeIds = Object.keys(previous)
if (currentWorktreeIds.length !== previousWorktreeIds.length) {
return false
}
for (const [worktreeIndex, worktreeId] of currentWorktreeIds.entries()) {
// Why: duplicate tab ids use first-worktree-wins lookup semantics, so a
// worktree-key reorder is a liveness change even when every array is reused.
if (previousWorktreeIds[worktreeIndex] !== worktreeId) {
return false
}
const currentTabs = current[worktreeId]
const previousTabs = previous[worktreeId]
if (currentTabs === previousTabs) {
continue
}
if (!currentTabs || !previousTabs || currentTabs.length !== previousTabs.length) {
return false
}
for (const [tabIndex, currentTab] of currentTabs.entries()) {
visitTab?.()
const previousTab = previousTabs[tabIndex]
if (
!previousTab ||
currentTab.id !== previousTab.id ||
currentTab.ptyId !== previousTab.ptyId
) {
return false
}
}
}
return true
}
function shouldSync(
current: AgentHookCompletionStoreSnapshot,
previous: AgentHookCompletionStoreSnapshot,
visitTab?: TabVisit
): boolean {
if (isTrackingEnabled(current) !== isTrackingEnabled(previous)) {
return true
}
if (
current.ptyIdsByTabId !== previous.ptyIdsByTabId ||
current.terminalLayoutsByTabId !== previous.terminalLayoutsByTabId ||
current.suppressedPtyExitIds !== previous.suppressedPtyExitIds
) {
return true
}
return !terminalTabLivenessMatches(current.tabsByWorktree, previous.tabsByWorktree, visitTab)
}
export function shouldSyncAgentHookCompletionForStoreUpdate(
current: AgentHookCompletionStoreSnapshot,
previous: AgentHookCompletionStoreSnapshot
): boolean {
return shouldSync(current, previous)
}
export function _measureAgentHookCompletionStoreSyncForTest(
current: AgentHookCompletionStoreSnapshot,
previous: AgentHookCompletionStoreSnapshot
): { shouldSync: boolean; tabVisits: number } {
let tabVisits = 0
const requiresSync = shouldSync(current, previous, () => {
tabVisits += 1
})
return { shouldSync: requiresSync, tabVisits }
}
+8 -6
View File
@@ -4299,7 +4299,7 @@ describe('useIpcEvents agent status snapshot integration', () => {
stateStartedAt: number
}
type StoreLike = Record<string, unknown>
type StoreSubscribeListener = (state: StoreLike) => void
type StoreSubscribeListener = (state: StoreLike, previousState: StoreLike) => void
type MobileFitEvent = {
ptyId: string
mode: 'mobile-fit' | 'desktop-fit'
@@ -4799,6 +4799,7 @@ describe('useIpcEvents agent status snapshot integration', () => {
expect(setAgentStatus).not.toHaveBeenCalled()
expect(getSnapshot).not.toHaveBeenCalled()
const previousStoreState = { ...storeState }
storeState.workspaceSessionReady = true
storeState.tabsByWorktree = {
'wt-1': [{ id: 'tab-future', ptyId: 'pty-1', worktreeId: 'wt-1', title: 'Future Tab' }]
@@ -4813,7 +4814,7 @@ describe('useIpcEvents agent status snapshot integration', () => {
if (typeof subscribeListenerRef.current !== 'function') {
throw new Error('Expected useAppStore.subscribe listener to be registered')
}
subscribeListenerRef.current(storeState)
subscribeListenerRef.current(storeState, previousStoreState)
await Promise.resolve()
expect(setAgentStatus).toHaveBeenCalledTimes(1)
@@ -4937,7 +4938,7 @@ describe('useIpcEvents agent status snapshot integration', () => {
vi.doMock('./agent-hook-completion-notifications', () => ({
observeAgentHookCompletionForNotification,
resetAgentHookCompletionNotificationCoordinators: vi.fn(),
syncAgentHookCompletionNotificationSettings: vi.fn()
syncAgentHookCompletionNotificationsForStoreUpdate: vi.fn()
}))
stubAuxiliaryModules()
vi.stubGlobal(
@@ -5014,7 +5015,7 @@ describe('useIpcEvents agent status snapshot integration', () => {
vi.doMock('./agent-hook-completion-notifications', () => ({
observeAgentHookCompletionForNotification,
resetAgentHookCompletionNotificationCoordinators: vi.fn(),
syncAgentHookCompletionNotificationSettings: vi.fn()
syncAgentHookCompletionNotificationsForStoreUpdate: vi.fn()
}))
stubAuxiliaryModules()
vi.stubGlobal(
@@ -5169,7 +5170,7 @@ describe('useIpcEvents agent status snapshot integration', () => {
vi.doMock('./agent-hook-completion-notifications', () => ({
observeAgentHookCompletionForNotification,
resetAgentHookCompletionNotificationCoordinators: vi.fn(),
syncAgentHookCompletionNotificationSettings: vi.fn()
syncAgentHookCompletionNotificationsForStoreUpdate: vi.fn()
}))
stubAuxiliaryModules()
vi.stubGlobal(
@@ -5978,6 +5979,7 @@ describe('useIpcEvents agent status snapshot integration', () => {
reason: 'unknown_tab_id'
})
const previousStoreState = { ...storeState }
storeState.terminalLayoutsByTabId = {
'tab-future': {
root: { type: 'leaf', leafId: FUTURE_LEAF_ID },
@@ -5988,7 +5990,7 @@ describe('useIpcEvents agent status snapshot integration', () => {
if (typeof subscribeListenerRef.current !== 'function') {
throw new Error('Expected useAppStore.subscribe listener to be registered')
}
subscribeListenerRef.current(storeState)
subscribeListenerRef.current(storeState, previousStoreState)
expect(setAgentStatus).toHaveBeenCalledTimes(2)
expect(setAgentStatus).toHaveBeenNthCalledWith(
+3 -3
View File
@@ -116,7 +116,7 @@ import {
import {
observeAgentHookCompletionForNotification,
resetAgentHookCompletionNotificationCoordinators,
syncAgentHookCompletionNotificationSettings
syncAgentHookCompletionNotificationsForStoreUpdate
} from './agent-hook-completion-notifications'
import { shouldSuppressCodexAutoApprovalStatus } from '@/components/terminal-pane/codex-auto-approval-notification-suppression'
import { showTerminalShortcutCaptureNotification } from '@/lib/terminal-shortcut-capture-notification'
@@ -3212,10 +3212,10 @@ export function useIpcEvents(): void {
// renderer state.
requestAgentStatusSnapshotIfReady()
unsubs.push(
useAppStore.subscribe(() => {
useAppStore.subscribe((state, previousState) => {
requestAgentStatusSnapshotIfReady()
flushPendingAgentStatuses()
syncAgentHookCompletionNotificationSettings()
syncAgentHookCompletionNotificationsForStoreUpdate(state, previousState)
})
)