fix: prevent stale cache when editing prompts across multiple windows

Add refreshGlobalStateKey() method to ContextProxy that reads fresh values
from VS Code's globalState before performing read-modify-write operations.

This fixes a bug where editing the context condensing prompt or mode prompts
would revert to a previous value when multiple VS Code windows are open,
because each window had its own stale cache.
This commit is contained in:
Hannes Rudolph 2025-12-17 20:52:41 -07:00
parent 06c5c7f980
commit 33f8e58833
3 changed files with 70 additions and 2 deletions

View file

@ -208,6 +208,19 @@ export class ContextProxy {
return this.originalContext.globalState.update(key, value)
}
/**
* Refresh a specific key from globalState and update the cache.
* This is useful for settings that may be edited across multiple VS Code windows,
* ensuring we read the latest value before making modifications.
* @param key The global state key to refresh
* @returns The fresh value from globalState
*/
refreshGlobalStateKey<K extends GlobalStateKey>(key: K): GlobalState[K] {
const value = this.originalContext.globalState.get<GlobalState[K]>(key)
this.stateCache[key] = value
return value as GlobalState[K]
}
private getAllGlobalState(): GlobalState {
return Object.fromEntries(GLOBAL_STATE_KEYS.map((key) => [key, this.getGlobalState(key)]))
}

View file

@ -185,6 +185,57 @@ describe("ContextProxy", () => {
})
})
describe("refreshGlobalStateKey", () => {
it("should read fresh value from globalState and update cache", async () => {
// Set up initial cache value
await proxy.updateGlobalState("customSupportPrompts", { OLD: "old-prompt" })
// Simulate another window updating globalState directly (bypassing our cache)
const newValue = { OLD: "old-prompt", NEW: "new-prompt" }
mockGlobalState.get.mockReturnValue(newValue)
// Refresh the key
const result = proxy.refreshGlobalStateKey("customSupportPrompts")
// Should return the fresh value from globalState
expect(result).toEqual(newValue)
// Should have updated the cache (subsequent getGlobalState should return new value)
const cachedValue = proxy.getGlobalState("customSupportPrompts")
expect(cachedValue).toEqual(newValue)
})
it("should return undefined when globalState has no value", async () => {
// Set up initial cache value
await proxy.updateGlobalState("customModePrompts", { code: { roleDefinition: "test" } })
// Simulate globalState being cleared (e.g., by another window)
mockGlobalState.get.mockReturnValue(undefined)
// Refresh the key
const result = proxy.refreshGlobalStateKey("customModePrompts")
// Should return undefined
expect(result).toBeUndefined()
// Cache should also be updated to undefined
const cachedValue = proxy.getGlobalState("customModePrompts")
expect(cachedValue).toBeUndefined()
})
it("should call globalState.get with the correct key", async () => {
// Clear mock call history
mockGlobalState.get.mockClear()
mockGlobalState.get.mockReturnValue({ ENHANCE: "test-prompt" })
// Refresh a specific key
proxy.refreshGlobalStateKey("customSupportPrompts")
// Should have called get with the correct key
expect(mockGlobalState.get).toHaveBeenCalledWith("customSupportPrompts")
})
})
describe("getSecret", () => {
it("should return value from cache when it exists", async () => {
// Manually set a value in the cache

View file

@ -1485,7 +1485,9 @@ export const webviewMessageHandler = async (
break
case "updatePrompt":
if (message.promptMode && message.customPrompt !== undefined) {
const existingPrompts = getGlobalState("customModePrompts") ?? {}
// Use refreshGlobalStateKey to get the latest value from globalState,
// avoiding stale cache issues when multiple VS Code windows are open.
const existingPrompts = provider.contextProxy.refreshGlobalStateKey("customModePrompts") ?? {}
const updatedPrompts = { ...existingPrompts, [message.promptMode]: message.customPrompt }
await updateGlobalState("customModePrompts", updatedPrompts)
const currentState = await provider.getStateToPostToWebview()
@ -1571,7 +1573,9 @@ export const webviewMessageHandler = async (
case "updateCondensingPrompt":
// Store the condensing prompt in customSupportPrompts["CONDENSE"]
// instead of customCondensingPrompt.
const currentSupportPrompts = getGlobalState("customSupportPrompts") ?? {}
// Use refreshGlobalStateKey to get the latest value from globalState,
// avoiding stale cache issues when multiple VS Code windows are open.
const currentSupportPrompts = provider.contextProxy.refreshGlobalStateKey("customSupportPrompts") ?? {}
const updatedSupportPrompts = { ...currentSupportPrompts, CONDENSE: message.text }
await updateGlobalState("customSupportPrompts", updatedSupportPrompts)
// Also update the old field for backward compatibility during migration.