fix: tighten sentinel quick verify waits

This commit is contained in:
Codex
2026-06-26 01:44:21 +00:00
parent 078bc61b0a
commit 999a593373
2 changed files with 161 additions and 39 deletions
@@ -343,7 +343,13 @@ async function processCommand(command) {
case "preflight": return preflightSummary();
case "goto": return withObserverSync(await gotoTarget(command.path || command.url || targetPath), "goto");
case "newSession": return withObserverSync(await createSessionFromUi(), "newSession");
case "sendPrompt": return withObserverSync(await sendPrompt(String(command.text || "")), "sendPrompt");
case "sendPrompt": return withObserverSync(await sendPrompt(String(command.text || ""), {
expectedAction: "turn",
responsePath: "/v1/agent/chat",
alternateResponsePaths: ["/v1/agent/chat/steer"],
noActiveReason: "send-no-turn-composer",
throwOnActionMismatch: true,
}), "sendPrompt");
case "steer": return withObserverSync(await sendPrompt(String(command.text || ""), { expectedAction: "steer", responsePath: "/v1/agent/chat/steer", noActiveReason: "steer-no-active-turn" }), "steer");
case "cancel": return withObserverSync(await cancelRunningTurn(), "cancel");
case "selectProvider": return withObserverSync(await selectProvider(String(command.provider || command.value || command.text || "")), "selectProvider");
@@ -1168,7 +1174,7 @@ async function sendPrompt(text, options = {}) {
if (composer.action !== options.expectedAction || composer.disabled === true) {
await clearComposerEditor(editor).catch(() => {});
const sideEffect = await waitForPromptSideEffect(beforeEvidence, 200).catch(() => promptSideEffectSnapshot());
return {
const blocked = {
beforeUrl,
afterUrl: currentPageUrl(),
textHash: sha256Text(text),
@@ -1191,13 +1197,20 @@ async function sendPrompt(text, options = {}) {
pageId,
valuesRedacted: true
};
if (options.throwOnActionMismatch === true) {
const error = new Error("sendPrompt composer action mismatch: expected " + options.expectedAction + " actual " + (composer.action || "null"));
error.details = blocked;
throw error;
}
return blocked;
}
}
const acceptedResponsePaths = [responsePath, ...(Array.isArray(options.alternateResponsePaths) ? options.alternateResponsePaths : [])];
const chatResponsePromise = page.waitForResponse((response) => {
const request = response.request();
if (request.method().toUpperCase() !== "POST") return false;
try {
return new URL(response.url()).pathname === responsePath;
return acceptedResponsePaths.includes(new URL(response.url()).pathname);
} catch {
return false;
}
@@ -1230,8 +1243,14 @@ async function sendPrompt(text, options = {}) {
throw error;
}
const chatStatus = chatResponse.status();
let chatUrlPath = responsePath;
try {
chatUrlPath = new URL(chatResponse.url()).pathname;
} catch {
chatUrlPath = responsePath;
}
if (chatStatus < 200 || chatStatus >= 300) {
throw new Error("sendPrompt observed POST " + responsePath + " HTTP " + chatStatus + " " + chatResponse.statusText());
throw new Error("sendPrompt observed POST " + chatUrlPath + " HTTP " + chatStatus + " " + chatResponse.statusText());
}
let chatPayload = null;
let chatPayloadError = null;
@@ -1240,12 +1259,6 @@ async function sendPrompt(text, options = {}) {
} catch (error) {
chatPayloadError = errorSummary(error);
}
let chatUrlPath = responsePath;
try {
chatUrlPath = new URL(chatResponse.url()).pathname;
} catch {
chatUrlPath = responsePath;
}
const payloadText = chatPayload ? JSON.stringify(chatPayload) : "";
const traceId = payloadText.match(/\btrc_[A-Za-z0-9_-]+\b/u)?.[0] || null;
const otelTraceId = typeof chatPayload?.otelTrace?.traceId === "string" && /^[0-9a-f]{32}$/u.test(chatPayload.otelTrace.traceId)
@@ -2496,6 +2509,9 @@ async function sampleOnePage(targetPage, { reason, groupSeq, pageRole, targetPag
const routeSessionMatch = url.match(/\/workbench\/sessions\/([^/?#]+)/u);
const activeSession = document.querySelector('[data-active="true"][data-session-id], [aria-selected="true"][data-session-id], .active[data-session-id]');
const activeSessionId = activeSession ? activeSession.getAttribute("data-session-id") : null;
const commandInput = document.querySelector("#command-input");
const commandSubmit = document.querySelector('#command-send, #command-submit, [data-testid="command-submit"], [data-testid="composer-submit"], [data-testid="send-command"]');
const composerWarning = document.querySelector(".composer-warning");
const messageSelector = 'article.message-card, .message-card[data-message-id], article[data-message-id]';
const stableTraceSelector = 'li.trace-render-row[data-row-id], li.trace-render-row[data-testid="trace-render-row"], [data-testid="trace-render-row"][data-row-id]';
const fallbackTraceSelector = '[data-testid*="trace" i], [class*="trace" i], [data-trace-id], [data-testid*="event" i]';
@@ -2573,6 +2589,19 @@ async function sampleOnePage(targetPage, { reason, groupSeq, pageRole, targetPag
sessionRail,
diagnostics,
turns,
composer: {
inputPresent: visible(commandInput),
inputDisabled: Boolean(commandInput?.disabled) || commandInput?.getAttribute("aria-disabled") === "true",
warningPresent: visible(composerWarning),
warningText: trim(composerWarning?.textContent || "", 160),
submitPresent: visible(commandSubmit),
submitDisabled: Boolean(commandSubmit?.disabled) || commandSubmit?.getAttribute("aria-disabled") === "true",
submitAction: commandSubmit?.getAttribute("data-action") || null,
submitText: trim(commandSubmit?.textContent || "", 80),
submitTestId: commandSubmit?.getAttribute("data-testid") || null,
activeStatus: activeSession?.getAttribute("data-status") || null,
valuesRedacted: true
},
projectManagement: collectProjectManagement(),
pageProvenance: {
url: location.href,