fix(web-probe): treat prompt side effects as submitted
This commit is contained in:
@@ -367,6 +367,7 @@ async function workbenchSessionSnapshot() {
|
||||
async function sendPrompt(text) {
|
||||
if (text.trim().length === 0) throw new Error("sendPrompt requires non-empty text");
|
||||
const beforeUrl = currentPageUrl();
|
||||
const beforeEvidence = await promptSideEffectSnapshot();
|
||||
const primaryEditor = page.locator("#command-input").last();
|
||||
const editor = await primaryEditor.isVisible().catch(() => false)
|
||||
? primaryEditor
|
||||
@@ -407,6 +408,17 @@ async function sendPrompt(text) {
|
||||
const chatResponse = await chatResponsePromise;
|
||||
await page.waitForTimeout(500);
|
||||
if (chatResponse?.waitError) {
|
||||
const sideEffect = await waitForPromptSideEffect(beforeEvidence, 12000);
|
||||
if (sideEffect.submitted) {
|
||||
return {
|
||||
beforeUrl,
|
||||
afterUrl: currentPageUrl(),
|
||||
textHash: sha256Text(text),
|
||||
textBytes: Buffer.byteLength(text),
|
||||
chatSubmit: { status: null, statusText: null, urlPath: "/v1/agent/chat", waitError: chatResponse.waitError, sideEffectObserved: true, sideEffect },
|
||||
pageId
|
||||
};
|
||||
}
|
||||
throw new Error("sendPrompt did not observe POST /v1/agent/chat response after submit: " + (chatResponse.waitError.message || chatResponse.waitError.name || "timeout"));
|
||||
}
|
||||
const chatStatus = chatResponse.status();
|
||||
@@ -422,6 +434,47 @@ async function sendPrompt(text) {
|
||||
return { beforeUrl, afterUrl: currentPageUrl(), textHash: sha256Text(text), textBytes: Buffer.byteLength(text), chatSubmit: { status: chatStatus, statusText: chatResponse.statusText(), urlPath: chatUrlPath }, pageId };
|
||||
}
|
||||
|
||||
async function waitForPromptSideEffect(beforeEvidence, timeoutMs) {
|
||||
await page.waitForFunction((before) => {
|
||||
const current = (() => {
|
||||
const text = document.body?.innerText || "";
|
||||
const runIds = Array.from(new Set(text.match(/run_[A-Za-z0-9_:-]+/gu) || [])).slice(-20);
|
||||
const traceIds = Array.from(new Set(text.match(/trc_[A-Za-z0-9_:-]+/gu) || [])).slice(-20);
|
||||
const running = /Trace running|最近\s*\d+\s*(?:秒|分钟|分|小时)前|Code Agent\s*耗时/iu.test(text);
|
||||
const executionError = /AgentRun error|agentrun:error:|provider-stream-disconnected|provider-unavailable/iu.test(text);
|
||||
const textBytes = new TextEncoder().encode(text).length;
|
||||
return { runIds, traceIds, running, executionError, textBytes };
|
||||
})();
|
||||
const beforeRuns = Array.isArray(before?.runIds) ? before.runIds : [];
|
||||
const beforeTraces = Array.isArray(before?.traceIds) ? before.traceIds : [];
|
||||
const newRun = current.runIds.some((id) => !beforeRuns.includes(id));
|
||||
const newTrace = current.traceIds.some((id) => !beforeTraces.includes(id));
|
||||
const meaningfulChange = Math.abs(current.textBytes - Number(before?.textBytes || 0)) > 20 && (current.running || current.executionError);
|
||||
return newRun || newTrace || meaningfulChange;
|
||||
}, beforeEvidence || {}, { timeout: timeoutMs }).catch(() => null);
|
||||
const after = await promptSideEffectSnapshot();
|
||||
const beforeRuns = Array.isArray(beforeEvidence?.runIds) ? beforeEvidence.runIds : [];
|
||||
const beforeTraces = Array.isArray(beforeEvidence?.traceIds) ? beforeEvidence.traceIds : [];
|
||||
const newRunIds = after.runIds.filter((id) => !beforeRuns.includes(id));
|
||||
const newTraceIds = after.traceIds.filter((id) => !beforeTraces.includes(id));
|
||||
const meaningfulChange = Math.abs(after.textBytes - Number(beforeEvidence?.textBytes || 0)) > 20 && (after.running || after.executionError);
|
||||
return { ...after, newRunIds, newTraceIds, submitted: newRunIds.length > 0 || newTraceIds.length > 0 || meaningfulChange, valuesRedacted: true };
|
||||
}
|
||||
|
||||
async function promptSideEffectSnapshot() {
|
||||
return page.evaluate(() => {
|
||||
const text = document.body?.innerText || "";
|
||||
return {
|
||||
runIds: Array.from(new Set(text.match(/run_[A-Za-z0-9_:-]+/gu) || [])).slice(-20),
|
||||
traceIds: Array.from(new Set(text.match(/trc_[A-Za-z0-9_:-]+/gu) || [])).slice(-20),
|
||||
running: /Trace running|最近\s*\d+\s*(?:秒|分钟|分|小时)前|Code Agent\s*耗时/iu.test(text),
|
||||
executionError: /AgentRun error|agentrun:error:|provider-stream-disconnected|provider-unavailable/iu.test(text),
|
||||
textBytes: new TextEncoder().encode(text).length,
|
||||
valuesRedacted: true
|
||||
};
|
||||
}).catch(() => ({ runIds: [], traceIds: [], running: false, executionError: false, textBytes: 0, valuesRedacted: true }));
|
||||
}
|
||||
|
||||
async function selectProvider(provider) {
|
||||
const target = String(provider || "").trim();
|
||||
if (!target) throw new Error("selectProvider requires provider name");
|
||||
|
||||
Reference in New Issue
Block a user