fix(sync): skip duplicate status events (#1152)

* fix(sync): skip duplicate status events

* test(sync): cover duplicate idle statuses

---------

Co-authored-by: Isaac Sanchez <isanchez-hawkins@arize.com>
Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
Isaac Sanchez-Hawkins
2026-05-08 15:56:18 +03:00
committed by GitHub
co-authored by Isaac Sanchez Bohdan Triapitsyn
parent c1c21fe340
commit 14c0bfe0bc
2 changed files with 89 additions and 3 deletions
@@ -1,5 +1,5 @@
import { describe, expect, test } from "bun:test"
import type { Event, Part, PermissionRequest, QuestionRequest } from "@opencode-ai/sdk/v2/client"
import type { Event, Part, PermissionRequest, QuestionRequest, SessionStatus } from "@opencode-ai/sdk/v2/client"
import { applyDirectoryEvent } from "../event-reducer"
import { INITIAL_STATE, type State } from "../types"
@@ -8,6 +8,7 @@ function state(overrides: Partial<State> = {}): State {
...INITIAL_STATE,
message: {},
part: {},
session_status: {},
...overrides,
}
}
@@ -87,6 +88,68 @@ describe("applyDirectoryEvent", () => {
expect(result).toBe(true)
})
test("skips duplicate session status events", () => {
const draft = state()
const busyStatus = { type: "busy" } as SessionStatus
const event = {
type: "session.status",
properties: { sessionID: "ses_1", status: busyStatus },
} as Event
expect(applyDirectoryEvent(draft, event)).toBe(true)
const statusRef = draft.session_status.ses_1
expect(applyDirectoryEvent(draft, event)).toBe(false)
expect(draft.session_status.ses_1).toBe(statusRef)
})
test("skips duplicate session idle events", () => {
const draft = state()
const event = {
type: "session.idle",
properties: { sessionID: "ses_1" },
} as Event
expect(applyDirectoryEvent(draft, event)).toBe(true)
const statusRef = draft.session_status.ses_1
expect(applyDirectoryEvent(draft, event)).toBe(false)
expect(draft.session_status.ses_1).toBe(statusRef)
})
test("skips duplicate session error idle-state events", () => {
const draft = state()
const event = {
type: "session.error",
properties: { sessionID: "ses_1" },
} as Event
expect(applyDirectoryEvent(draft, event)).toBe(true)
const statusRef = draft.session_status.ses_1
expect(applyDirectoryEvent(draft, event)).toBe(false)
expect(draft.session_status.ses_1).toBe(statusRef)
})
test("detects retry status metadata changes", () => {
const draft = state({
session_status: {
ses_1: { type: "retry", attempt: 1, message: "rate limited", next: 10 } as SessionStatus,
},
})
const event = {
type: "session.status",
properties: {
sessionID: "ses_1",
status: { type: "retry", attempt: 2, message: "rate limited", next: 20 } as SessionStatus,
},
} as Event
expect(applyDirectoryEvent(draft, event)).toBe(true)
expect((draft.session_status.ses_1 as Extract<SessionStatus, { type: "retry" }>).attempt).toBe(2)
})
test("updates permission request arrays immutably", () => {
const initialPermissions = [
{ id: "perm_1", sessionID: "ses_1" } as PermissionRequest,
+25 -2
View File
@@ -90,6 +90,18 @@ function shouldPreserveExistingPart(previous: Part, next: Part): boolean {
return false
}
function areSessionStatusesEqual(left: SessionStatus | undefined, right: SessionStatus): boolean {
if (left === right) return true
if (!left || left.type !== right.type) return false
if (left.type === "retry") {
return right.type === "retry"
&& left.attempt === right.attempt
&& left.message === right.message
&& left.next === right.next
}
return true
}
// ---------------------------------------------------------------------------
// Global events
// ---------------------------------------------------------------------------
@@ -219,19 +231,30 @@ export function applyDirectoryEvent(
case "session.status": {
const props = event.properties as { sessionID: string; status: SessionStatus }
if (areSessionStatusesEqual(draft.session_status[props.sessionID], props.status)) {
return false
}
draft.session_status[props.sessionID] = props.status
return true
}
case "session.idle": {
const props = event.properties as { sessionID: string }
draft.session_status[props.sessionID] = { type: "idle" }
const status = { type: "idle" } as const
if (areSessionStatusesEqual(draft.session_status[props.sessionID], status)) {
return false
}
draft.session_status[props.sessionID] = status
return true
}
case "session.error": {
const props = event.properties as { sessionID: string }
draft.session_status[props.sessionID] = { type: "idle" }
const status = { type: "idle" } as const
if (areSessionStatusesEqual(draft.session_status[props.sessionID], status)) {
return false
}
draft.session_status[props.sessionID] = status
return true
}