Merge pull request #4231 from code-yeongyu/fix/post-4228-test-and-session-gone
Fix post-4228 test and session-gone follow-ups
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { describe, expect, mock, test } from "bun:test"
|
||||
|
||||
import type { OpencodeClient } from "./opencode-client"
|
||||
import { verifySessionExists } from "./session-existence"
|
||||
import { checkSessionExistence, verifySessionExists } from "./session-existence"
|
||||
import { unsafeTestValue } from "../../../test-support/unsafe-test-value"
|
||||
|
||||
describe("verifySessionExists", () => {
|
||||
@@ -24,4 +24,20 @@ describe("verifySessionExists", () => {
|
||||
query: { directory: "/project/root" },
|
||||
})
|
||||
})
|
||||
|
||||
test("classifies transient lookup errors as unknown", async () => {
|
||||
const get = mock(async () => ({
|
||||
error: { message: "Network timeout", status: 500 },
|
||||
data: undefined,
|
||||
}))
|
||||
const client = unsafeTestValue<OpencodeClient>({
|
||||
session: {
|
||||
get,
|
||||
},
|
||||
})
|
||||
|
||||
const result = await checkSessionExistence(client, "session-123")
|
||||
|
||||
expect(result).toBe("unknown")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { OpencodeClient } from "./opencode-client"
|
||||
|
||||
export const MIN_SESSION_GONE_POLLS = 3
|
||||
export type SessionExistenceStatus = "exists" | "missing" | "unknown"
|
||||
|
||||
function extractErrorMessage(error: unknown): string | undefined {
|
||||
if (typeof error === "string") {
|
||||
@@ -35,11 +36,11 @@ function isSessionNotFoundError(error: unknown): boolean {
|
||||
return message.includes("not found") || message.includes("missing")
|
||||
}
|
||||
|
||||
export async function verifySessionExists(
|
||||
export async function checkSessionExistence(
|
||||
client: OpencodeClient,
|
||||
sessionID: string,
|
||||
directory?: string
|
||||
): Promise<boolean> {
|
||||
): Promise<SessionExistenceStatus> {
|
||||
try {
|
||||
const response = await client.session.get({
|
||||
path: { id: sessionID },
|
||||
@@ -47,11 +48,19 @@ export async function verifySessionExists(
|
||||
})
|
||||
|
||||
if (response.error !== undefined && response.error !== null) {
|
||||
return !isSessionNotFoundError(response.error)
|
||||
return isSessionNotFoundError(response.error) ? "missing" : "unknown"
|
||||
}
|
||||
|
||||
return response.data != null
|
||||
return response.data != null ? "exists" : "missing"
|
||||
} catch (error) {
|
||||
return !isSessionNotFoundError(error)
|
||||
return isSessionNotFoundError(error) ? "missing" : "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
export async function verifySessionExists(
|
||||
client: OpencodeClient,
|
||||
sessionID: string,
|
||||
directory?: string
|
||||
): Promise<boolean> {
|
||||
return await checkSessionExistence(client, sessionID, directory) !== "missing"
|
||||
}
|
||||
|
||||
@@ -472,7 +472,7 @@ describe("checkAndInterruptStaleTasks", () => {
|
||||
expect(mockClient.session.get).toHaveBeenCalledWith({ path: { id: "ses-1" } })
|
||||
})
|
||||
|
||||
it("should NOT cancel task when session.get returns a transient error response", async () => {
|
||||
it("should NOT cancel or reset missed polls when session.get returns a transient error response", async () => {
|
||||
//#given - repeated missing polls but lookup failed with a retryable transport error
|
||||
const task = createRunningTask({
|
||||
startedAt: new Date(Date.now() - 300_000),
|
||||
@@ -500,7 +500,7 @@ describe("checkAndInterruptStaleTasks", () => {
|
||||
|
||||
//#then
|
||||
expect(task.status).toBe("running")
|
||||
expect(task.consecutiveMissedPolls).toBe(0)
|
||||
expect(task.consecutiveMissedPolls).toBe(3)
|
||||
expect(mockClient.session.get).toHaveBeenCalledWith({ path: { id: "ses-1" } })
|
||||
})
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
} from "./constants"
|
||||
import { abortWithTimeout } from "./abort-with-timeout"
|
||||
import { removeTaskToastTracking } from "./remove-task-toast-tracking"
|
||||
import { MIN_SESSION_GONE_POLLS, verifySessionExists } from "./session-existence"
|
||||
import { checkSessionExistence, MIN_SESSION_GONE_POLLS } from "./session-existence"
|
||||
|
||||
import { isActiveSessionStatus } from "./session-status-classifier"
|
||||
import { getSessionActivityFromClient, type SessionActivityResolver } from "./session-activity"
|
||||
@@ -178,9 +178,13 @@ export async function checkAndInterruptStaleTasks(args: {
|
||||
if (activityRefresh.type === "activity" && now - activityRefresh.activityTime <= effectiveTimeout) continue
|
||||
}
|
||||
|
||||
if (sessionGone && await verifySessionExists(client, sessionID, directory)) {
|
||||
task.consecutiveMissedPolls = 0
|
||||
continue
|
||||
if (sessionGone) {
|
||||
const existence = await checkSessionExistence(client, sessionID, directory)
|
||||
if (existence === "exists") {
|
||||
task.consecutiveMissedPolls = 0
|
||||
continue
|
||||
}
|
||||
if (existence === "unknown") continue
|
||||
}
|
||||
|
||||
const staleMinutes = Math.round(runtime / 60000)
|
||||
@@ -228,9 +232,13 @@ export async function checkAndInterruptStaleTasks(args: {
|
||||
|
||||
if (task.status !== "running") continue
|
||||
|
||||
if (sessionGone && await verifySessionExists(client, sessionID, directory)) {
|
||||
task.consecutiveMissedPolls = 0
|
||||
continue
|
||||
if (sessionGone) {
|
||||
const existence = await checkSessionExistence(client, sessionID, directory)
|
||||
if (existence === "exists") {
|
||||
task.consecutiveMissedPolls = 0
|
||||
continue
|
||||
}
|
||||
if (existence === "unknown") continue
|
||||
}
|
||||
|
||||
const staleMinutes = Math.round(timeSinceLastUpdate / 60000)
|
||||
|
||||
@@ -231,7 +231,7 @@ describe("remapAgentKeysToDisplayNames", () => {
|
||||
const result = remapAgentKeysToDisplayNames(agents, overrides)
|
||||
|
||||
// then the legacy AGENT_DISPLAY_NAMES value is used
|
||||
expect(result["Sisyphus - Ultraworker"]).toBeDefined()
|
||||
expect(result[getAgentListDisplayName("sisyphus")]).toBeDefined()
|
||||
expect(result["总指挥"]).toBeUndefined()
|
||||
})
|
||||
|
||||
@@ -245,7 +245,7 @@ describe("remapAgentKeysToDisplayNames", () => {
|
||||
const result = remapAgentKeysToDisplayNames(agents)
|
||||
|
||||
// then the legacy AGENT_DISPLAY_NAMES value is used
|
||||
expect(result["Sisyphus - Ultraworker"]).toBeDefined()
|
||||
expect(result[getAgentListDisplayName("sisyphus")]).toBeDefined()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user