fix(pi): recover from Works prompt rejection

This commit is contained in:
2026-08-25 14:50:10 +08:00
parent 4580568384
commit 5f0ef761ea
9 changed files with 439 additions and 31 deletions

View File

@@ -143,6 +143,11 @@ function assertProxyStatus(status) {
|| status?.processes?.supported !== true
|| status?.processes?.parent?.length !== 1
|| status?.processes?.child?.length !== 1
|| status?.providerCompatibility?.roleContract?.developerRejected !== true
|| status?.providerCompatibility?.roleContract?.systemAccepted !== true
|| status?.providerCompatibility?.developerRoleRequests !== 0
|| status?.providerCompatibility?.systemRoleRequests < 2
|| status?.providerCompatibility?.controlledDefiniteRejections !== 1
|| Object.values(status?.tokenSafety ?? {}).some((safe) => safe !== true)) {
throw new Error(`Packaged Main proxy status is incomplete: ${JSON.stringify(status)}`);
}
@@ -157,6 +162,10 @@ function assertProxyProof(proof) {
|| proof?.firstConversation?.runStatus !== 'idle'
|| proof?.firstConversation?.userInputProjected !== true
|| proof?.firstConversation?.parentResponseProjected !== true
|| proof?.firstConversation?.definiteRejectionObserved !== true
|| proof?.firstConversation?.retryAccepted !== true
|| proof?.providerCompatibility?.developerRoleRequests !== 0
|| proof?.providerCompatibility?.controlledDefiniteRejections !== 1
|| proof?.currentHostTokenUsedBy?.join(',') !== 'parent,child'
|| proof?.released?.workers !== 0
|| Object.values(proof?.tokenSafety ?? {}).some((safe) => safe !== true)) {
@@ -164,19 +173,25 @@ function assertProxyProof(proof) {
}
}
async function waitForActiveProxyProof(electronApplication) {
async function waitForProxyProof(electronApplication, predicate, message) {
const deadline = Date.now() + 30_000;
let latest;
while (Date.now() < deadline) {
latest = await evaluateProof(electronApplication, 'proxy.status');
if (latest?.proxy?.activeProviderRequests?.child === 1
&& latest?.proxy?.processes?.parent?.length === 1
&& latest?.proxy?.processes?.child?.length === 1) {
return latest;
}
if (predicate(latest?.proxy)) return latest;
await new Promise((resolveWait) => setTimeout(resolveWait, 200));
}
throw new Error(`Packaged Main proxy child did not become active: ${JSON.stringify(latest?.proxy)}`);
throw new Error(`${message}: ${JSON.stringify(latest?.proxy)}`);
}
async function waitForActiveProxyProof(electronApplication) {
return await waitForProxyProof(
electronApplication,
(status) => status?.activeProviderRequests?.child === 1
&& status?.processes?.parent?.length === 1
&& status?.processes?.child?.length === 1,
'Packaged Main proxy child did not become active',
);
}
async function waitForResilienceProof(electronApplication, predicate, message) {
@@ -380,7 +395,9 @@ export async function runPackagedProductProof(options) {
await page.evaluate(() => { window.location.hash = '/chat'; });
const composer = page.getByRole('textbox');
await composer.waitFor({ state: 'visible', timeout: 30_000 });
await composer.fill('Exercise the packaged Main proxy Conversation');
const rejectedPrompt = 'Exercise the packaged Main proxy rejection';
const retryPrompt = 'Retry the packaged Main proxy Conversation';
await composer.fill(rejectedPrompt);
if (!await composer.isEnabled()) throw new Error('Packaged proxy first Conversation composer is disabled');
const beforeSubmitText = await page.locator('body').innerText();
if (beforeSubmitText.includes('本地编程运行时暂时不可用')
@@ -389,6 +406,42 @@ export async function runPackagedProductProof(options) {
}
await page.getByRole('button', { name: '发送' }).click({ timeout: 30_000 });
const rejectedProxy = await waitForProxyProof(
electronApplication,
(status) => status?.providerCompatibility?.controlledDefiniteRejections === 1,
'Packaged Main did not observe the controlled definite rejection',
);
const draftDeadline = Date.now() + 10_000;
while (Date.now() < draftDeadline && await composer.inputValue() !== rejectedPrompt) {
await new Promise((resolveWait) => setTimeout(resolveWait, 100));
}
const draftRestored = await composer.inputValue() === rejectedPrompt;
const rejectionText = await page.locator('body').innerText();
const submissionErrorSafe = !rejectionText.includes('controlled definite model rejection')
&& !rejectionText.includes('developer role is unsupported')
&& !rejectionText.includes('X-Works-Square-AI-Token')
&& !rejectionText.includes('Authorization');
if (!draftRestored || !submissionErrorSafe) {
throw new Error(
`Packaged definite rejection did not restore a safe draft: ${JSON.stringify({ draftRestored, submissionErrorSafe })}`,
);
}
if (await page.getByRole('button', { name: '重试' }).count()) {
throw new Error('Packaged definite submission rejection was exposed as a runtime recovery error');
}
await composer.fill(retryPrompt);
const sendButton = page.getByRole('button', { name: '发送' });
const retryDeadline = Date.now() + 10_000;
while (Date.now() < retryDeadline && !await sendButton.isEnabled()) {
await new Promise((resolveWait) => setTimeout(resolveWait, 100));
}
const retryAfterEditEnabled = await sendButton.isEnabled();
if (!retryAfterEditEnabled) {
const bodyText = await page.locator('body').innerText();
throw new Error(`Packaged proxy retry stayed disabled after editing: ${bodyText}`);
}
await sendButton.click({ timeout: 30_000 });
let proxyStatus;
try {
proxyStatus = await waitForActiveProxyProof(electronApplication);
@@ -590,11 +643,16 @@ export async function runPackagedProductProof(options) {
},
proxy: {
setup: proxyStart.proxy,
rejected: rejectedProxy.proxy,
active: proxyStatus.proxy,
completed: proxyFinish.proxy,
ui: {
firstConversationEditable: true,
inputSubmitted: true,
definiteRejectionObserved: true,
draftRestored,
submissionErrorSafe,
retryAfterEditEnabled,
retrySubmitted: true,
runtimeUnavailableBanner: false,
permanentRecovering: false,
},