fix(sync): drop orphan session parts (#1183)

* fix(sync): drop orphan session parts

* fix(sync): guard missing part cache

---------

Co-authored-by: Isaac Sanchez <isanchez-hawkins@arize.com>
This commit is contained in:
Isaac Sanchez-Hawkins
2026-05-12 11:05:37 +03:00
committed by GitHub
co-authored by Isaac Sanchez
parent b944bd812c
commit e50a484633
2 changed files with 29 additions and 2 deletions
@@ -1,11 +1,11 @@
import { describe, expect, test } from "bun:test"
import type { Message, PermissionRequest, QuestionRequest } from "@opencode-ai/sdk/v2/client"
import type { Message, Part, PermissionRequest, QuestionRequest } from "@opencode-ai/sdk/v2/client"
import {
canDisposeDirectory,
hasPendingBlockingRequests,
pickDirectoriesToEvict,
} from "../eviction"
import { getProtectedSessionCacheIds, pickSessionCacheEvictions } from "../session-cache"
import { dropSessionCaches, getProtectedSessionCacheIds, pickSessionCacheEvictions } from "../session-cache"
import { INITIAL_STATE, type DirState, type State } from "../types"
const DAY_MS = 24 * 60 * 60 * 1000
@@ -172,4 +172,20 @@ describe("session cache eviction", () => {
expect(seen.has("ses_question")).toBe(true)
expect(seen.has("ses_streaming")).toBe(true)
})
test("drops parts for evicted messages without part session ids", () => {
const store = buildState({
message: {
ses_old: [{ id: "msg_1", role: "user", time: { created: 1 } } as Message],
},
part: {
msg_1: [{ id: "prt_1", messageID: "msg_1" } as Part],
},
})
dropSessionCaches(store, ["ses_old"])
expect(store.message.ses_old).toBe(undefined)
expect(store.part.msg_1).toBe(undefined)
})
})
+11
View File
@@ -56,6 +56,17 @@ export function dropSessionCaches(store: SessionCache, sessionIDs: Iterable<stri
const stale = new Set(Array.from(sessionIDs).filter(Boolean))
if (stale.size === 0) return
const staleMessageIDs = new Set<string>()
for (const sessionID of stale) {
for (const message of store.message?.[sessionID] ?? []) {
if (message?.id) staleMessageIDs.add(message.id)
}
}
for (const messageID of staleMessageIDs) {
if (store.part) delete store.part[messageID]
}
for (const key of Object.keys(store.part ?? {})) {
const parts = store.part[key]
if (!parts?.some((part) => stale.has((part as { sessionID?: string })?.sessionID ?? "")))