Merge pull request #1049 from pikasTech/fix/1020-newsession-requestfailed-retry

修复 #1020 newSession 网络切换后重试
This commit is contained in:
Lyon
2026-06-26 22:33:33 +08:00
committed by GitHub
@@ -1189,41 +1189,113 @@ function isProjectManagementPathname(value) {
return projectManagement.targetPaths.some((target) => pathname === target || pathname.startsWith(target + "/"));
}
function isAgentSessionCreateRequest(requestOrUrl) {
const method = typeof requestOrUrl?.method === "function" ? requestOrUrl.method().toUpperCase() : "";
if (method && method !== "POST") return false;
const url = typeof requestOrUrl === "string" ? requestOrUrl : typeof requestOrUrl?.url === "function" ? requestOrUrl.url() : "";
try {
return new URL(url).pathname === "/v1/agent/sessions";
} catch {
return false;
}
}
function requestFailureSummary(request) {
let failure = null;
try {
failure = request.failure();
} catch {}
let urlPath = null;
try {
urlPath = new URL(request.url()).pathname;
} catch {}
return {
method: typeof request.method === "function" ? request.method().toUpperCase() : null,
urlPath,
failureText: failure?.errorText || null,
valuesRedacted: true
};
}
async function clickAndWaitForAgentSessionCreate(create) {
let removeRequestFailedListener = () => {};
const requestFailedPromise = new Promise((resolve) => {
let timeout = null;
const handler = (request) => {
if (!isAgentSessionCreateRequest(request)) return;
removeRequestFailedListener();
resolve({ kind: "requestfailed", requestFailure: requestFailureSummary(request) });
};
removeRequestFailedListener = () => {
page.off("requestfailed", handler);
if (timeout !== null) clearTimeout(timeout);
timeout = null;
};
page.on("requestfailed", handler);
timeout = setTimeout(() => {
removeRequestFailedListener();
resolve(null);
}, 45000);
});
const createResponsePromise = page.waitForResponse((response) => {
const request = response.request();
return isAgentSessionCreateRequest(request) || isAgentSessionCreateRequest(response.url());
}, { timeout: 45000 }).then((response) => ({ kind: "response", response })).catch((error) => ({ kind: "wait-error", waitError: errorSummary(error) }));
await create.click();
const outcome = await Promise.race([createResponsePromise, requestFailedPromise]);
removeRequestFailedListener();
return outcome ?? await createResponsePromise;
}
async function createSessionFromUi() {
const beforeUrl = currentPageUrl();
const before = await workbenchSessionSnapshot();
const readinessBeforeClick = await workbenchReadinessSnapshot(page);
const create = page.locator("#session-create").first();
await create.waitFor({ state: "visible", timeout: 15000 });
const createButtonState = await create.evaluate((element) => {
const rect = element.getBoundingClientRect();
const style = window.getComputedStyle(element);
return {
tag: element.tagName.toLowerCase(),
id: element.id || null,
disabled: Boolean(element.disabled),
ariaDisabled: element.getAttribute("aria-disabled") || null,
visible: rect.width > 0 && rect.height > 0 && style.visibility !== "hidden" && style.display !== "none",
rect: { width: Math.round(rect.width), height: Math.round(rect.height) },
valuesRedacted: true
};
}).catch((error) => ({ error: errorSummary(error), valuesRedacted: true }));
const createResponsePromise = page.waitForResponse((response) => {
const request = response.request();
if (request.method().toUpperCase() !== "POST") return false;
try {
return new URL(response.url()).pathname === "/v1/agent/sessions";
} catch {
return false;
const attempts = [];
let createResponse = null;
for (let attempt = 1; attempt <= 2; attempt += 1) {
const readinessBeforeClick = await workbenchReadinessSnapshot(page);
const create = page.locator("#session-create").first();
await create.waitFor({ state: "visible", timeout: 15000 });
const createButtonState = await create.evaluate((element) => {
const rect = element.getBoundingClientRect();
const style = window.getComputedStyle(element);
return {
tag: element.tagName.toLowerCase(),
id: element.id || null,
disabled: Boolean(element.disabled),
ariaDisabled: element.getAttribute("aria-disabled") || null,
visible: rect.width > 0 && rect.height > 0 && style.visibility !== "hidden" && style.display !== "none",
rect: { width: Math.round(rect.width), height: Math.round(rect.height) },
valuesRedacted: true
};
}).catch((error) => ({ error: errorSummary(error), valuesRedacted: true }));
const outcome = await clickAndWaitForAgentSessionCreate(create);
if (outcome?.kind === "response") {
createResponse = outcome.response;
attempts.push({ attempt, outcome: "response", readinessBeforeClick, createButtonState, valuesRedacted: true });
break;
}
}, { timeout: 45000 }).catch((error) => ({ waitError: errorSummary(error) }));
await create.click();
const createResponse = await createResponsePromise;
if (createResponse?.waitError) {
const error = new Error("newSession did not observe POST /v1/agent/sessions response after click: " + (createResponse.waitError.message || createResponse.waitError.name || "timeout"));
error.details = { beforeUrl, afterUrl: currentPageUrl(), before, readinessBeforeClick, createButtonState, waitError: createResponse.waitError, pageId, valuesRedacted: true };
const afterAttempt = await workbenchSessionSnapshot();
attempts.push({
attempt,
outcome: outcome?.kind || "unknown",
readinessBeforeClick,
createButtonState,
waitError: outcome?.waitError || null,
requestFailure: outcome?.requestFailure || null,
after: afterAttempt,
valuesRedacted: true
});
if (attempt < 2 && outcome?.kind === "requestfailed") {
await page.waitForTimeout(1500);
continue;
}
const waitError = outcome?.waitError || outcome?.requestFailure || { name: outcome?.kind || "unknown", valuesRedacted: true };
const error = new Error("newSession did not observe POST /v1/agent/sessions response after click: " + (waitError.message || waitError.failureText || waitError.name || "timeout"));
error.details = { beforeUrl, afterUrl: currentPageUrl(), before, attempts, pageId, valuesRedacted: true };
throw error;
}
if (createResponse === null) throw new Error("newSession did not produce an authoritative session create response");
const createStatus = createResponse.status();
let createPayload = null;
let createPayloadError = null;
@@ -1252,7 +1324,7 @@ async function createSessionFromUi() {
const ok = Boolean(afterSessionId === createdSessionId && after?.routeSessionId === createdSessionId && after?.composerReady);
if (!ok) {
const error = new Error("newSession did not select the authoritative newly created workbench session");
error.details = { beforeUrl, afterUrl: currentPageUrl(), before, after, createdSessionId, pageId, valuesRedacted: true };
error.details = { beforeUrl, afterUrl: currentPageUrl(), before, after, createdSessionId, attempts, pageId, valuesRedacted: true };
throw error;
}
return {
@@ -1261,6 +1333,7 @@ async function createSessionFromUi() {
ok,
before,
after,
attempts,
sessionId: createdSessionId,
createSession: { status: createStatus, statusText: createResponse.statusText(), responseParsed: createPayload !== null, responseParseError: createPayloadError, createdSessionId, valuesRedacted: true },
pageId