fix: retry web probe prompt fill

This commit is contained in:
Codex
2026-06-28 09:44:19 +00:00
parent 8d8dd7b90f
commit 3cb5322aeb
@@ -1877,7 +1877,7 @@ async function sendPrompt(text, options = {}) {
error.details = { beforeUrl, afterUrl: currentPageUrl(), controlRecovery, composerRecovery, snapshot, editorWaitError: errorSummary(editorWaitError), pageId, pageEpoch: controlPageEpoch, valuesRedacted: true };
throw error;
}
await fillComposerEditor(editor, text);
editor = await fillComposerEditorWithRetry(editor, text, { beforeUrl, controlRecovery });
const primarySubmitSelector = '#command-send, #command-submit, [data-testid="command-submit"], [data-testid="composer-submit"], [data-testid="send-command"]';
const primarySubmit = page.locator(primarySubmitSelector).last();
const submit = await primarySubmit.isVisible().catch(() => false)
@@ -2030,19 +2030,83 @@ async function sendPrompt(text, options = {}) {
};
}
async function fillComposerEditor(editor, text) {
async function fillComposerEditorWithRetry(initialEditor, text, context = {}) {
let editor = initialEditor;
let lastError = null;
let recovery = null;
for (let attempt = 1; attempt <= 2; attempt += 1) {
try {
if (attempt > 1 || !editor) editor = await resolveComposerEditor(8000);
await fillComposerEditor(editor, text, { timeoutMs: 12000 });
const retained = await composerEditorRetainsText(editor, text);
if (retained) return editor;
throw new Error("sendPrompt composer editor did not retain filled text");
} catch (error) {
lastError = error;
await appendJsonl(files.control, eventRecord("sendPrompt-composer-fill-retry", {
attempt,
willRetry: attempt < 2,
error: errorSummary(error),
beforeUrl: context.beforeUrl || null,
afterUrl: currentPageUrl(),
controlRecovery: context.controlRecovery || null,
pageId,
pageEpoch: controlPageEpoch,
valuesRedacted: true
}));
if (attempt >= 2) break;
recovery = await forceRecoverControlPageForCommand("sendPrompt-composer-fill-failed");
if (recovery.ok !== true) await page.waitForTimeout(250);
editor = null;
}
}
const snapshot = await controlPageLivenessSnapshot("sendPrompt-composer-fill-failed-final", 3000);
const error = new Error("sendPrompt composer editor fill failed");
error.details = {
beforeUrl: context.beforeUrl || null,
afterUrl: currentPageUrl(),
controlRecovery: context.controlRecovery || null,
recovery,
snapshot,
fillError: errorSummary(lastError),
pageId,
pageEpoch: controlPageEpoch,
valuesRedacted: true
};
throw error;
}
async function resolveComposerEditor(timeoutMs = 8000) {
const primaryEditor = page.locator("#command-input").last();
const candidate = await primaryEditor.isVisible().catch(() => false)
? primaryEditor
: page.locator('textarea, [role="textbox"], [contenteditable="true"], input[type="text"]').last();
await candidate.waitFor({ state: "visible", timeout: timeoutMs });
return candidate;
}
async function fillComposerEditor(editor, text, options = {}) {
const timeoutMs = Number.isFinite(Number(options.timeoutMs)) ? Math.max(1000, Math.trunc(Number(options.timeoutMs))) : 30000;
const tag = await editor.evaluate((element) => element.tagName.toLowerCase()).catch(() => "");
const editable = await editor.evaluate((element) => element.getAttribute("contenteditable") === "true").catch(() => false);
if (tag === "textarea" || tag === "input") await editor.fill(text);
if (tag === "textarea" || tag === "input") await editor.fill(text, { timeout: timeoutMs });
else if (editable) {
await editor.click();
await editor.click({ timeout: timeoutMs });
await page.keyboard.insertText(text);
} else {
await editor.click();
await editor.click({ timeout: timeoutMs });
await page.keyboard.insertText(text);
}
}
async function composerEditorRetainsText(editor, expectedText) {
const expected = String(expectedText || "");
return editor.evaluate((element, value) => {
if (element instanceof HTMLTextAreaElement || element instanceof HTMLInputElement) return element.value === value;
return (element.textContent || "") === value;
}, expected).catch(() => false);
}
async function clearComposerEditor(editor) {
const tag = await editor.evaluate((element) => element.tagName.toLowerCase()).catch(() => "");
const editable = await editor.evaluate((element) => element.getAttribute("contenteditable") === "true").catch(() => false);