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:
committed by
GitHub
co-authored by
Isaac Sanchez
Bohdan Triapitsyn
parent
c1c21fe340
commit
14c0bfe0bc
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, test } from "bun:test"
|
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 { applyDirectoryEvent } from "../event-reducer"
|
||||||
import { INITIAL_STATE, type State } from "../types"
|
import { INITIAL_STATE, type State } from "../types"
|
||||||
|
|
||||||
@@ -8,6 +8,7 @@ function state(overrides: Partial<State> = {}): State {
|
|||||||
...INITIAL_STATE,
|
...INITIAL_STATE,
|
||||||
message: {},
|
message: {},
|
||||||
part: {},
|
part: {},
|
||||||
|
session_status: {},
|
||||||
...overrides,
|
...overrides,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,6 +88,68 @@ describe("applyDirectoryEvent", () => {
|
|||||||
expect(result).toBe(true)
|
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", () => {
|
test("updates permission request arrays immutably", () => {
|
||||||
const initialPermissions = [
|
const initialPermissions = [
|
||||||
{ id: "perm_1", sessionID: "ses_1" } as PermissionRequest,
|
{ id: "perm_1", sessionID: "ses_1" } as PermissionRequest,
|
||||||
|
|||||||
@@ -90,6 +90,18 @@ function shouldPreserveExistingPart(previous: Part, next: Part): boolean {
|
|||||||
return false
|
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
|
// Global events
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -219,19 +231,30 @@ export function applyDirectoryEvent(
|
|||||||
|
|
||||||
case "session.status": {
|
case "session.status": {
|
||||||
const props = event.properties as { sessionID: string; status: SessionStatus }
|
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
|
draft.session_status[props.sessionID] = props.status
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
case "session.idle": {
|
case "session.idle": {
|
||||||
const props = event.properties as { sessionID: string }
|
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
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
case "session.error": {
|
case "session.error": {
|
||||||
const props = event.properties as { sessionID: string }
|
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
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user