diff --git a/scripts/src/hwlab-node-web-observe-runner-source.ts b/scripts/src/hwlab-node-web-observe-runner-source.ts index 387aa77e..403dca33 100644 --- a/scripts/src/hwlab-node-web-observe-runner-source.ts +++ b/scripts/src/hwlab-node-web-observe-runner-source.ts @@ -684,21 +684,47 @@ async function createSessionFromUi() { const before = await workbenchSessionSnapshot(); const create = page.locator("#session-create").first(); await create.waitFor({ state: "visible", timeout: 15000 }); + 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; + } + }, { timeout: 45000 }).catch((error) => ({ waitError: errorSummary(error) })); await create.click(); - await page.waitForFunction((initial) => { + const createResponse = await createResponsePromise; + if (createResponse?.waitError) throw new Error("newSession did not observe POST /v1/agent/sessions response after click: " + (createResponse.waitError.message || createResponse.waitError.name || "timeout")); + const createStatus = createResponse.status(); + let createPayload = null; + let createPayloadError = null; + try { + createPayload = await createResponse.json(); + } catch (error) { + createPayloadError = errorSummary(error); + } + const createdSessionId = sessionIdFromAgentSessionPayload(createPayload); + if (createStatus < 200 || createStatus >= 300 || !createdSessionId) { + const error = new Error("newSession did not receive an authoritative session id from POST /v1/agent/sessions"); + error.details = { status: createStatus, statusText: createResponse.statusText(), responseParsed: createPayload !== null, responseParseError: createPayloadError, createdSessionId, valuesRedacted: true }; + throw error; + } + await page.waitForFunction((expectedSessionId) => { const activeTab = document.querySelector(".session-tab[data-active='true'], .session-tab[aria-selected='true']"); const sessionId = activeTab?.getAttribute("data-session-id") || ""; + const routeMatch = window.location.pathname.match(/\/workbench\/sessions\/([^/]+)/u) || window.location.pathname.match(/\/workspace\/sessions\/([^/]+)/u); + const routeSessionId = routeMatch ? decodeURIComponent(routeMatch[1] || "") : ""; const warning = document.querySelector(".composer-warning")?.textContent?.trim() || ""; const input = document.querySelector("#command-input"); - return Boolean(activeTab && sessionId && sessionId !== initial.sessionId && input && !input.disabled && !warning); - }, { sessionId: before?.activeSessionId || "" }, { timeout: 30000 }).catch(() => null); + return Boolean(activeTab && sessionId === expectedSessionId && routeSessionId === expectedSessionId && input && !input.disabled && !warning); + }, createdSessionId, { timeout: 45000 }).catch(() => null); const after = await workbenchSessionSnapshot(); - const beforeSessionId = before?.activeSessionId || before?.routeSessionId || ""; const afterSessionId = after?.activeSessionId || after?.routeSessionId || ""; - const ok = Boolean(afterSessionId && afterSessionId !== beforeSessionId && after?.composerReady); + const ok = Boolean(afterSessionId === createdSessionId && after?.routeSessionId === createdSessionId && after?.composerReady); if (!ok) { - const error = new Error("newSession did not create or select a different workbench session"); - error.details = { beforeUrl, afterUrl: currentPageUrl(), before, after, pageId, valuesRedacted: true }; + 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 }; throw error; } return { @@ -707,11 +733,20 @@ async function createSessionFromUi() { ok, before, after, - sessionId: afterSessionId || null, + sessionId: createdSessionId, + createSession: { status: createStatus, statusText: createResponse.statusText(), responseParsed: createPayload !== null, responseParseError: createPayloadError, createdSessionId, valuesRedacted: true }, pageId }; } +function sessionIdFromAgentSessionPayload(payload) { + const direct = payload?.sessionId ?? payload?.id ?? payload?.session?.sessionId ?? payload?.session?.id ?? payload?.data?.sessionId ?? payload?.data?.id ?? payload?.data?.session?.sessionId ?? payload?.data?.session?.id; + const directText = String(direct || "").trim(); + if (/^ses_[A-Za-z0-9_-]+$/u.test(directText)) return directText; + const match = JSON.stringify(payload ?? "").match(/\bses_[A-Za-z0-9_-]+\b/u); + return match ? match[0] : null; +} + async function workbenchSessionSnapshot(targetPage = page) { return targetPage.evaluate(() => { const activeTab = document.querySelector(".session-tab[data-active='true'], .session-tab[aria-selected='true']");