mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-29 03:44:06 +08:00
fix(vscode): prevent draftKey effect from recreating deleted-session drafts
handleSessionDeleted cleared the draft Maps inside the cleanup batch, but PromptInput's createEffect(on(draftKey, ...)) runs after the batch ends. When the active session was deleted, the effect saw draftKey transition from ...:session:<id> to ...:pending:<id> (draftSessionID was left pointing at the deleted id) and called saveDraft(prev, currentText, currentImages), which wrote the unsent draft and attached image data URLs straight back into the just-cleared ...:session:<id> entry. Two changes: - Move deleteDraftsForSession(sessionID) out of the batch so it runs after the effect's recreate is also cleaned up. - Clear draftSessionID alongside currentSessionID in the active-session delete branch so draftKey falls all the way to the 'new' bucket instead of ...:pending:<deleted-id>. Tests: regression in prompt-drafts.test.ts covering the recreate-then- cleanup path, plus contract assertions in prompt-send-contract.test.ts that setDraftSessionID is cleared and deleteDraftsForSession is called outside the batch.
This commit is contained in:
@@ -30,6 +30,25 @@ describe("deleteDraftsForSession", () => {
|
||||
deleteDraftsForSession("")
|
||||
expect(drafts.get("prompt:default:session:a")).toBe("draft a")
|
||||
})
|
||||
|
||||
it("clears drafts that PromptInput's draftKey effect recreates after the batch", () => {
|
||||
const img = {
|
||||
id: "i1",
|
||||
filename: "x.png",
|
||||
mime: "image/png",
|
||||
dataUrl: "data:image/png;base64,AAAA",
|
||||
}
|
||||
drafts.set("prompt:default:session:a", "draft a")
|
||||
imageDrafts.set("prompt:default:session:a", [img])
|
||||
deleteDraftsForSession("a")
|
||||
drafts.set("prompt:default:session:a", "draft a")
|
||||
imageDrafts.set("prompt:default:session:a", [img])
|
||||
|
||||
deleteDraftsForSession("a")
|
||||
|
||||
expect(drafts.has("prompt:default:session:a")).toBe(false)
|
||||
expect(imageDrafts.has("prompt:default:session:a")).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("sessionDraftKey", () => {
|
||||
|
||||
@@ -116,3 +116,23 @@ describe("isPromptBlocked signature contract", () => {
|
||||
expect(params).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe("handleSessionDeleted draft cleanup contract", () => {
|
||||
const source = readFile(SESSION_FILE)
|
||||
|
||||
it("clears draftSessionID alongside currentSessionID when deleting the active session", () => {
|
||||
const body = extractFunctionBody(source, "handleSessionDeleted")
|
||||
const activeBlock = body.match(/if \(currentSessionID\(\) === sessionID\) \{([\s\S]*?)\}/)
|
||||
expect(activeBlock).not.toBeNull()
|
||||
expect(activeBlock![1]).toContain("setDraftSessionID(undefined)")
|
||||
})
|
||||
|
||||
it("calls deleteDraftsForSession outside the cleanup batch so PromptInput's recreate is also cleaned up", () => {
|
||||
const body = extractFunctionBody(source, "handleSessionDeleted")
|
||||
const batchMatch = body.match(/batch\(\(\) => \{([\s\S]*?)\}\)/)
|
||||
expect(batchMatch).not.toBeNull()
|
||||
expect(batchMatch![1]).not.toContain("deleteDraftsForSession(sessionID)")
|
||||
const postBatch = body.slice((batchMatch!.index ?? 0) + batchMatch![0].length)
|
||||
expect(postBatch).toContain("deleteDraftsForSession(sessionID)")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1911,12 +1911,13 @@ export const SessionProvider: ParentComponent = (props) => {
|
||||
delete map[sessionID]
|
||||
}),
|
||||
)
|
||||
deleteDraftsForSession(sessionID)
|
||||
if (currentSessionID() === sessionID) {
|
||||
setCurrentSessionID(undefined)
|
||||
setDraftSessionID(undefined)
|
||||
setLoading(false)
|
||||
}
|
||||
})
|
||||
deleteDraftsForSession(sessionID)
|
||||
}
|
||||
|
||||
// Splices the message from the store and deletes its parts.
|
||||
|
||||
@@ -18,4 +18,4 @@ export function deleteDraftsForSession(id: string) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user