mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-25 17:18:11 +00:00
Two structural fixes from the Desktop performance audit (P2 tier): 1. Scope live tool-diff subscriptions. `ToolEntry` subscribed to the whole `$toolDiffs` map via `useStore`, so one `recordToolDiff` re-rendered every mounted tool row. Add a cached per-toolCallId derived atom (`$toolInlineDiff(id)`, mirroring the existing `$toolDisclosureOpen` pattern); computed() only notifies when that id's diff string changes, so a live patch re-renders one row. 2. Narrow profile / gateway-switch query invalidation. Both the active-profile subscription and `wipeSessionListsForGatewaySwitch` called keyless `queryClient.invalidateQueries()`, refetching account/marketplace/onboarding caches on every switch. Add `invalidateProfileScopedQueries()` with a denylist of profile-independent roots (billing, marketplace-themes, onboarding-model-options, contrib-logs-tail). A denylist is correctness-safe: a root we forget just refetches (cheap), whereas an allowlist that misses a profile-scoped key would paint the previous profile's data. Tests: per-tool notify isolation, and real-QueryClient invalidation partition (profile-scoped invalidated, global left intact, unknown keys invalidated).
58 lines
1.7 KiB
TypeScript
58 lines
1.7 KiB
TypeScript
import { beforeEach, describe, expect, it } from 'vitest'
|
|
|
|
import { invalidateProfileScopedQueries, queryClient } from './query-client'
|
|
|
|
function invalidated(key: unknown[]): boolean {
|
|
return queryClient.getQueryState(key)?.isInvalidated ?? false
|
|
}
|
|
|
|
describe('invalidateProfileScopedQueries', () => {
|
|
beforeEach(() => {
|
|
queryClient.clear()
|
|
})
|
|
|
|
it('invalidates profile-scoped caches and leaves account/global caches intact', () => {
|
|
const profileScoped = [
|
|
['hermes-config-record'],
|
|
['hermes-config-schema'],
|
|
['skills-list'],
|
|
['toolsets-list'],
|
|
['model-options', 'global'],
|
|
['command-palette', 'sessions'],
|
|
['session-picker', 'sessions']
|
|
]
|
|
|
|
const global = [
|
|
['billing', 'state'],
|
|
['billing', 'subscription'],
|
|
['marketplace-themes', 'all'],
|
|
['marketplace-themes-settings', 'x'],
|
|
['onboarding-model-options', 'y'],
|
|
['contrib-logs-tail']
|
|
]
|
|
|
|
for (const key of [...profileScoped, ...global]) {
|
|
queryClient.setQueryData(key, { seeded: true })
|
|
}
|
|
|
|
invalidateProfileScopedQueries()
|
|
|
|
for (const key of profileScoped) {
|
|
expect(invalidated(key), `${JSON.stringify(key)} should be invalidated`).toBe(true)
|
|
}
|
|
|
|
for (const key of global) {
|
|
expect(invalidated(key), `${JSON.stringify(key)} should be left intact`).toBe(false)
|
|
}
|
|
})
|
|
|
|
it('invalidates unknown/non-string-rooted keys by default (correctness-safe)', () => {
|
|
queryClient.setQueryData(['some-future-profile-query'], 1)
|
|
queryClient.setQueryData([{ scope: 'weird' }], 1)
|
|
|
|
invalidateProfileScopedQueries()
|
|
|
|
expect(invalidated(['some-future-profile-query'])).toBe(true)
|
|
expect(invalidated([{ scope: 'weird' }])).toBe(true)
|
|
})
|
|
})
|