mirror of
https://github.com/stablyai/orca.git
synced 2026-10-09 08:02:35 +00:00
perf(renderer): FIFO-cap recentlyClosedAgentStatusTabIds (#7645)
recentlyClosedAgentStatusTabIds (agent-status store) suppresses late hook/status events for a just-closed terminal tab. It was only ever added to — one `true` entry per agent-tab close, keyed by the ephemeral tabId, never deleted or capped — so it grew for the renderer's whole session. It's the renderer twin of the main-process closedAgentStatusTabIds set that #7561 already FIFO-capped. Fix: bound it to the 1024 most-recent closed tabs via delete-then-set LRU with oldest-key eviction (Record key order is insertion order), mirroring #7561. A status event for a tab closed >1024 tabs ago cannot still arrive, so suppression behavior is unchanged. Test (red->green): closing 1029 tabs leaves exactly 1024 markers with the oldest evicted and the most-recent retained; without the cap all 1029 persist.
This commit is contained in:
@@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { AgentStatusEntry } from '../../../../shared/agent-status-types'
|
||||
import type { TerminalTab } from '../../../../shared/types'
|
||||
import type { RetainedAgentEntry } from './agent-status'
|
||||
import { RECENTLY_CLOSED_AGENT_STATUS_TAB_IDS_MAX } from './agent-status'
|
||||
import { createTestStore } from './store-test-helpers'
|
||||
|
||||
// Why: dropAgentStatus and dismissRetainedAgentsByWorktree mirror the renderer-
|
||||
@@ -119,6 +120,24 @@ describe('dropAgentStatusByTabPrefix -> IPC fan-out', () => {
|
||||
'tab-new': true
|
||||
})
|
||||
})
|
||||
|
||||
it('FIFO-caps recentlyClosedAgentStatusTabIds so it cannot grow unbounded', () => {
|
||||
stubWindowApi()
|
||||
const store = createTestStore()
|
||||
const cap = RECENTLY_CLOSED_AGENT_STATUS_TAB_IDS_MAX
|
||||
|
||||
for (let i = 0; i < cap + 5; i++) {
|
||||
store.getState().dropAgentStatusByTabPrefix(`tab-${i}`)
|
||||
}
|
||||
|
||||
const closed = store.getState().recentlyClosedAgentStatusTabIds
|
||||
expect(Object.keys(closed)).toHaveLength(cap)
|
||||
// Oldest evicted, most-recent retained (a status event for a tab closed
|
||||
// >cap tabs ago cannot still arrive, so suppression is unaffected).
|
||||
expect(closed['tab-0']).toBeUndefined()
|
||||
expect(closed['tab-4']).toBeUndefined()
|
||||
expect(closed[`tab-${cap + 4}`]).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('dismissRetainedAgentsByWorktree → IPC fan-out', () => {
|
||||
|
||||
@@ -733,6 +733,35 @@ function getLaunchConfigForEntry(
|
||||
: undefined
|
||||
}
|
||||
|
||||
// Why: the renderer twin of the main-process closedAgentStatusTabIds set that
|
||||
// #7561 FIFO-capped. It suppresses late hook/status events for a just-closed tab,
|
||||
// so it must outlive the tab briefly — but tabId is ephemeral and it was only
|
||||
// ever added to, growing one entry per tab-close for the renderer's whole life.
|
||||
export const RECENTLY_CLOSED_AGENT_STATUS_TAB_IDS_MAX = 1024
|
||||
|
||||
// delete-then-set for LRU recency, then evict the oldest keys past the cap (Record
|
||||
// key order is insertion order for non-integer string keys). A status event for a
|
||||
// tab closed >MAX tabs ago cannot still arrive, so eviction is safe.
|
||||
function boundRecentlyClosedAgentStatusTabIds(
|
||||
existing: Record<string, true>,
|
||||
tabId: string
|
||||
): Record<string, true> {
|
||||
const next: Record<string, true> = {}
|
||||
for (const key of Object.keys(existing)) {
|
||||
if (key !== tabId) {
|
||||
next[key] = true
|
||||
}
|
||||
}
|
||||
next[tabId] = true
|
||||
const keys = Object.keys(next)
|
||||
if (keys.length > RECENTLY_CLOSED_AGENT_STATUS_TAB_IDS_MAX) {
|
||||
for (const stale of keys.slice(0, keys.length - RECENTLY_CLOSED_AGENT_STATUS_TAB_IDS_MAX)) {
|
||||
delete next[stale]
|
||||
}
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
function getLaunchConfigForStatusMetadata(
|
||||
state: AppState,
|
||||
metadata: AgentLaunchConfigStatusMetadata
|
||||
@@ -1632,10 +1661,10 @@ export const createAgentStatusSlice: StateCreator<AppState, [], [], AgentStatusS
|
||||
delete nextAck[k]
|
||||
}
|
||||
}
|
||||
const nextClosedTabs: Record<string, true> = {
|
||||
...s.recentlyClosedAgentStatusTabIds,
|
||||
[tabIdPrefix]: true
|
||||
}
|
||||
const nextClosedTabs = boundRecentlyClosedAgentStatusTabIds(
|
||||
s.recentlyClosedAgentStatusTabIds,
|
||||
tabIdPrefix
|
||||
)
|
||||
|
||||
if (
|
||||
liveKeys.length === 0 &&
|
||||
|
||||
Reference in New Issue
Block a user