Skip to content

Commit 813a909

Browse files
committed
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.
1 parent d274812 commit 813a909

File tree

3 files changed

+70
-2
lines changed

3 files changed

+70
-2
lines changed

src/core/config/ContextProxy.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,19 @@ export class ContextProxy {
208208
return this.originalContext.globalState.update(key, value)
209209
}
210210

211+
/**
212+
* Refresh a specific key from globalState and update the cache.
213+
* This is useful for settings that may be edited across multiple VS Code windows,
214+
* ensuring we read the latest value before making modifications.
215+
* @param key The global state key to refresh
216+
* @returns The fresh value from globalState
217+
*/
218+
refreshGlobalStateKey<K extends GlobalStateKey>(key: K): GlobalState[K] {
219+
const value = this.originalContext.globalState.get<GlobalState[K]>(key)
220+
this.stateCache[key] = value
221+
return value as GlobalState[K]
222+
}
223+
211224
private getAllGlobalState(): GlobalState {
212225
return Object.fromEntries(GLOBAL_STATE_KEYS.map((key) => [key, this.getGlobalState(key)]))
213226
}

src/core/config/__tests__/ContextProxy.spec.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,57 @@ describe("ContextProxy", () => {
185185
})
186186
})
187187

188+
describe("refreshGlobalStateKey", () => {
189+
it("should read fresh value from globalState and update cache", async () => {
190+
// Set up initial cache value
191+
await proxy.updateGlobalState("customSupportPrompts", { OLD: "old-prompt" })
192+
193+
// Simulate another window updating globalState directly (bypassing our cache)
194+
const newValue = { OLD: "old-prompt", NEW: "new-prompt" }
195+
mockGlobalState.get.mockReturnValue(newValue)
196+
197+
// Refresh the key
198+
const result = proxy.refreshGlobalStateKey("customSupportPrompts")
199+
200+
// Should return the fresh value from globalState
201+
expect(result).toEqual(newValue)
202+
203+
// Should have updated the cache (subsequent getGlobalState should return new value)
204+
const cachedValue = proxy.getGlobalState("customSupportPrompts")
205+
expect(cachedValue).toEqual(newValue)
206+
})
207+
208+
it("should return undefined when globalState has no value", async () => {
209+
// Set up initial cache value
210+
await proxy.updateGlobalState("customModePrompts", { code: { roleDefinition: "test" } })
211+
212+
// Simulate globalState being cleared (e.g., by another window)
213+
mockGlobalState.get.mockReturnValue(undefined)
214+
215+
// Refresh the key
216+
const result = proxy.refreshGlobalStateKey("customModePrompts")
217+
218+
// Should return undefined
219+
expect(result).toBeUndefined()
220+
221+
// Cache should also be updated to undefined
222+
const cachedValue = proxy.getGlobalState("customModePrompts")
223+
expect(cachedValue).toBeUndefined()
224+
})
225+
226+
it("should call globalState.get with the correct key", async () => {
227+
// Clear mock call history
228+
mockGlobalState.get.mockClear()
229+
mockGlobalState.get.mockReturnValue({ ENHANCE: "test-prompt" })
230+
231+
// Refresh a specific key
232+
proxy.refreshGlobalStateKey("customSupportPrompts")
233+
234+
// Should have called get with the correct key
235+
expect(mockGlobalState.get).toHaveBeenCalledWith("customSupportPrompts")
236+
})
237+
})
238+
188239
describe("getSecret", () => {
189240
it("should return value from cache when it exists", async () => {
190241
// Manually set a value in the cache

src/core/webview/webviewMessageHandler.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,7 +1485,9 @@ export const webviewMessageHandler = async (
14851485
break
14861486
case "updatePrompt":
14871487
if (message.promptMode && message.customPrompt !== undefined) {
1488-
const existingPrompts = getGlobalState("customModePrompts") ?? {}
1488+
// Use refreshGlobalStateKey to get the latest value from globalState,
1489+
// avoiding stale cache issues when multiple VS Code windows are open.
1490+
const existingPrompts = provider.contextProxy.refreshGlobalStateKey("customModePrompts") ?? {}
14891491
const updatedPrompts = { ...existingPrompts, [message.promptMode]: message.customPrompt }
14901492
await updateGlobalState("customModePrompts", updatedPrompts)
14911493
const currentState = await provider.getStateToPostToWebview()
@@ -1571,7 +1573,9 @@ export const webviewMessageHandler = async (
15711573
case "updateCondensingPrompt":
15721574
// Store the condensing prompt in customSupportPrompts["CONDENSE"]
15731575
// instead of customCondensingPrompt.
1574-
const currentSupportPrompts = getGlobalState("customSupportPrompts") ?? {}
1576+
// Use refreshGlobalStateKey to get the latest value from globalState,
1577+
// avoiding stale cache issues when multiple VS Code windows are open.
1578+
const currentSupportPrompts = provider.contextProxy.refreshGlobalStateKey("customSupportPrompts") ?? {}
15751579
const updatedSupportPrompts = { ...currentSupportPrompts, CONDENSE: message.text }
15761580
await updateGlobalState("customSupportPrompts", updatedSupportPrompts)
15771581
// Also update the old field for backward compatibility during migration.

0 commit comments

Comments
 (0)