Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-sync-filter-lifecycle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---

Prevent duplicate-event tracking from suppressing delayed sync events after reconnects or high event bursts.
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ export class KiloConnectionService {
private remoteService: import("../RemoteStatusService").RemoteStatusService | null = null

private readonly eventListeners: Set<SSEEventListener> = new Set()
private readonly duplicateEvent = createDuplicateEventFilter()
private readonly stateListeners: Set<StateListener> = new Set()
private readonly notificationDismissListeners: Set<NotificationDismissListener> = new Set()
private readonly languageChangeListeners: Set<LanguageChangeListener> = new Set()
Expand Down Expand Up @@ -821,6 +820,7 @@ export class KiloConnectionService {
},
})
const sse = new SdkSSEAdapter(client)
const duplicateEvent = createDuplicateEventFilter()
this.client = client
this.sseClient = sse

Expand All @@ -839,7 +839,7 @@ export class KiloConnectionService {
sse.onEvent((event, directory) => {
if (this.sseClient !== sse) return
// EventV2Bridge also emits these durable compatibility envelopes after their normal live events.
if (this.duplicateEvent(event)) return
if (duplicateEvent(event)) return
this.handlePermissionEvent(event, directory)
this.handleQuestionEvent(event, directory)
for (const listener of this.eventListeners) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ export function createDuplicateEventFilter() {
}

if (duplicateLiveEvents.has(event.type)) {
if (seen.size >= DUPLICATE_EVENT_LIMIT) seen.delete(seen.values().next().value!)
seen.add(event.id)
if (seen.size > DUPLICATE_EVENT_LIMIT) seen.delete(seen.values().next().value!)
}
return false
}
Expand Down
128 changes: 127 additions & 1 deletion packages/kilo-vscode/tests/unit/connection-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ describe("resolveEventSessionId", () => {
})
})

describe("isDuplicateSyncEvent", () => {
describe("createDuplicateEventFilter", () => {
it("drops a compatibility envelope only after its live event", () => {
const filter = createDuplicateEventFilter()
const live = {
Expand Down Expand Up @@ -233,4 +233,130 @@ describe("isDuplicateSyncEvent", () => {
),
).toBe(false)
})

it("continues tracking new live events after the cap is reached", () => {
const filter = createDuplicateEventFilter()
for (let index = 0; index < 1024; index++) {
expect(
filter({
id: `live-${index}`,
type: "message.part.updated",
properties: { sessionID: "s6", part, delta: "x" },
}),
).toBe(false)
}

expect(
filter({
id: "live-1024",
type: "message.part.updated",
properties: { sessionID: "s6", part, delta: "x" },
}),
).toBe(false)
expect(
filter(
sync({
type: "sync",
name: "message.part.updated.1",
id: "live-1024",
seq: 9,
aggregateID: "s6",
data: { sessionID: "s6", part, time: 0 },
}),
),
).toBe(true)
expect(
filter(
sync({
type: "sync",
name: "message.part.updated.1",
id: "live-0",
seq: 8,
aggregateID: "s6",
data: { sessionID: "s6", part, time: 0 },
}),
),
).toBe(false)
expect(
filter(
sync({
type: "sync",
name: "message.part.updated.1",
id: "live-1024",
seq: 9,
aggregateID: "s6",
data: { sessionID: "s6", part, time: 0 },
}),
),
).toBe(false)
})

it("forwards delayed envelopes for evicted IDs", () => {
const filter = createDuplicateEventFilter()
for (let index = 0; index < 1024; index++) {
expect(
filter({
id: `pending-${index}`,
type: "message.part.updated",
properties: { sessionID: "s6", part, delta: "x" },
}),
).toBe(false)
}

expect(
filter({
id: "overflow",
type: "message.part.updated",
properties: { sessionID: "s6", part, delta: "x" },
}),
).toBe(false)
expect(
filter(
sync({
type: "sync",
name: "message.part.updated.1",
id: "pending-0",
seq: 8,
aggregateID: "s6",
data: { sessionID: "s6", part, time: 0 },
}),
),
).toBe(false)
expect(
filter(
sync({
type: "sync",
name: "message.part.updated.1",
id: "pending-1023",
seq: 9,
aggregateID: "s6",
data: { sessionID: "s6", part, time: 0 },
}),
),
).toBe(true)
})

it("does not carry duplicate IDs between connections", () => {
const first = createDuplicateEventFilter()
const second = createDuplicateEventFilter()
const live = {
id: "connection-event",
type: "message.part.updated",
properties: { sessionID: "s6", part, delta: "x" },
} satisfies Payload

expect(first(live)).toBe(false)
expect(
second(
sync({
type: "sync",
name: "message.part.updated.1",
id: "connection-event",
seq: 11,
aggregateID: "s6",
data: { sessionID: "s6", part, time: 0 },
}),
),
).toBe(false)
})
})
Loading