fix(pi): validate user-entry conversation forks
This commit is contained in:
@@ -164,6 +164,16 @@ function assertProxyProof(proof) {
|
||||
|| proof?.firstConversation?.parentResponseProjected !== true
|
||||
|| proof?.firstConversation?.definiteRejectionObserved !== true
|
||||
|| proof?.firstConversation?.retryAccepted !== true
|
||||
|| proof?.fork?.conversationCount !== 2
|
||||
|| proof?.fork?.sameAgent !== true
|
||||
|| proof?.fork?.targetBindingEstablished !== true
|
||||
|| proof?.fork?.distinctSessionBinding !== true
|
||||
|| proof?.fork?.workerStatus !== 'ready'
|
||||
|| proof?.fork?.runStatus !== 'idle'
|
||||
|| proof?.fork?.sourceEntryRole !== 'user'
|
||||
|| proof?.fork?.targetHydratedBeforeSourceEntry !== true
|
||||
|| proof?.fork?.sourceUnchanged !== true
|
||||
|| proof?.fork?.promptReplayObserved !== false
|
||||
|| proof?.providerCompatibility?.developerRoleRequests !== 0
|
||||
|| proof?.providerCompatibility?.controlledDefiniteRejections !== 1
|
||||
|| proof?.currentHostTokenUsedBy?.join(',') !== 'parent,child'
|
||||
@@ -460,6 +470,43 @@ export async function runPackagedProductProof(options) {
|
||||
}
|
||||
if (!await composer.isEnabled()) throw new Error('Packaged proxy composer is not editable after the turn');
|
||||
|
||||
const settledProxy = await evaluateProof(electronApplication, 'proxy.status');
|
||||
if (settledProxy?.proxy?.conversationCount !== 1
|
||||
|| settledProxy?.proxy?.activeProviderRequests?.parent !== 0
|
||||
|| settledProxy?.proxy?.activeProviderRequests?.child !== 0) {
|
||||
throw new Error(`Packaged proxy source did not settle before fork: ${JSON.stringify(settledProxy?.proxy)}`);
|
||||
}
|
||||
const timeline = page.getByTestId('coding-conversation-timeline');
|
||||
const forkActions = timeline.getByRole('button', { name: '从这里创建新对话分支' });
|
||||
await forkActions.waitFor({ state: 'visible', timeout: 30_000 });
|
||||
if (await forkActions.count() !== 1) {
|
||||
throw new Error(`Packaged proxy timeline exposed ${await forkActions.count()} fork actions for one user and one assistant entry`);
|
||||
}
|
||||
const sourceHeader = await page.getByTestId('coding-conversation-header').innerText();
|
||||
await forkActions.click({ timeout: 30_000 });
|
||||
await page.waitForFunction((previous) => {
|
||||
const header = document.querySelector('[data-testid="coding-conversation-header"]');
|
||||
return Boolean(header?.textContent?.includes('(fork)') && header.textContent !== previous);
|
||||
}, sourceHeader, { timeout: 30_000 });
|
||||
const forkedHeader = await page.getByTestId('coding-conversation-header').innerText();
|
||||
const forkedTitle = forkedHeader.split('\n', 1)[0]?.trim();
|
||||
if (!forkedTitle?.includes('(fork)')) {
|
||||
throw new Error(`Packaged proxy fork target was not selected: ${forkedHeader}`);
|
||||
}
|
||||
const agentConversations = page.getByRole('group', { name: 'Packaged proxy proof agent 的对话' });
|
||||
await agentConversations.getByRole('button', { name: forkedTitle, exact: true })
|
||||
.waitFor({ state: 'visible', timeout: 30_000 });
|
||||
const forkedBody = await page.locator('body').innerText();
|
||||
if (forkedBody.includes('本地编程运行时暂时不可用')
|
||||
|| forkedBody.includes('正在重新连接本地 Agent')) {
|
||||
throw new Error(`Packaged user-entry fork exposed a runtime banner: ${forkedBody}`);
|
||||
}
|
||||
if (await timeline.getByText(retryPrompt, { exact: true }).count() !== 0
|
||||
|| await timeline.getByText('REAL_PARENT_COMPLETE', { exact: true }).count() !== 0
|
||||
|| await timeline.getByRole('button', { name: '从这里创建新对话分支' }).count() !== 0) {
|
||||
throw new Error('Packaged fork target did not hydrate at the path before the selected user entry');
|
||||
}
|
||||
|
||||
const proxyFinish = await evaluateProof(electronApplication, 'proxy.finish');
|
||||
proxyActive = false;
|
||||
assertPackagedMain(proxyFinish);
|
||||
@@ -653,6 +700,12 @@ export async function runPackagedProductProof(options) {
|
||||
submissionErrorSafe,
|
||||
retryAfterEditEnabled,
|
||||
retrySubmitted: true,
|
||||
assistantForkActionHidden: true,
|
||||
userForkActionAvailable: true,
|
||||
forkTargetSelected: true,
|
||||
sameAgentOwnership: true,
|
||||
sourceUnchanged: proxyFinish.proxy?.fork?.sourceUnchanged === true,
|
||||
uncertainPromptReplayed: proxyFinish.proxy?.fork?.promptReplayObserved === true,
|
||||
runtimeUnavailableBanner: false,
|
||||
permanentRecovering: false,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user