test(pi): separate submit rejection proof

This commit is contained in:
2026-08-25 15:00:52 +08:00
parent 5f0ef761ea
commit e3debc9b33
2 changed files with 35 additions and 17 deletions

View File

@@ -6,6 +6,7 @@ import path from 'node:path';
import { promisify } from 'node:util';
import type { CodingProductComposition } from '../../api/coding-product-services';
import { CodingConversationServiceError } from '../conversation-service';
import { getRecentLogs } from '../../utils/logger';
import { createCodingConversationStore } from '../../coding-projects/conversation-store';
import { createCodingProjectAgent } from '../../coding-projects/project-config';
@@ -57,7 +58,7 @@ type ProviderRequest = {
toolNames: string[];
hasToolResult: boolean;
firstMessageRole: string | null;
outcome: 'accepted' | 'controlled_rejection' | 'developer_role_rejection';
outcome: 'accepted' | 'developer_role_rejection';
};
type HeldProviderResponse = {
@@ -104,6 +105,8 @@ type ProxyCompositionRun = {
projectId: string;
projectPath: string;
hostToken: string;
originalAcceptPrompt: CodingProductComposition['conversations']['acceptPrompt'];
submissionInjection: { definiteRejections: number };
activeStatus?: PiReleaseProxyCompositionStatus;
};
@@ -448,7 +451,6 @@ async function startLocalProofProvider(mode: ProofProviderMode): Promise<LocalPr
const held = new Set<HeldProviderResponse>();
let closed = false;
let closeFlight: Promise<void> | null = null;
let controlledSubmitRejectionIssued = false;
const server: Server = createServer(async (request, response) => {
response.once('error', () => undefined);
try {
@@ -486,16 +488,6 @@ async function startLocalProofProvider(mode: ProofProviderMode): Promise<LocalPr
rejectProofRequest(response, 'developer role is unsupported by the controlled endpoint');
return;
}
if (mode === 'submit-rejection'
&& role === 'parent'
&& !toolResult
&& !controlledSubmitRejectionIssued) {
controlledSubmitRejectionIssued = true;
providerRequest.outcome = 'controlled_rejection';
rejectProofRequest(response, 'controlled definite model rejection');
return;
}
if (mode === 'resilience') {
if (role === 'child') {
if (toolResult) respondWithText(response, model, 'RESILIENCE_CHILD_COMPLETE');
@@ -914,9 +906,7 @@ async function proxyCompositionStatus(
systemRoleRequests: run.provider.requests.filter(({ firstMessageRole }) => (
firstMessageRole === 'system'
)).length,
controlledDefiniteRejections: run.provider.requests.filter(({ outcome }) => (
outcome === 'controlled_rejection'
)).length,
controlledDefiniteRejections: run.submissionInjection.definiteRejections,
};
const parent = processInspection.processes
.filter(({ role }) => role === 'parent')
@@ -1342,6 +1332,7 @@ export async function runFinalAsarExtensionProof(): Promise<PiReleaseExtensionPr
}
async function cleanupProxyCompositionRun(run: ProxyCompositionRun): Promise<void> {
run.composition.conversations.acceptPrompt = run.originalAcceptPrompt;
run.provider.releaseAll();
await run.composition.projects.removeProject(run.projectId).catch(() => undefined);
await run.provider.close().catch(() => undefined);
@@ -1400,12 +1391,30 @@ export async function startFinalAsarProxyCompositionProof(input: {
prompt: 'Follow the controlled loopback Provider and complete its subagent request.',
skillIds: [],
});
const originalAcceptPrompt = input.composition.conversations.acceptPrompt
.bind(input.composition.conversations);
const submissionInjection = { definiteRejections: 0 };
let rejectNextSubmission = true;
input.composition.conversations.acceptPrompt = async (promptInput) => {
if (rejectNextSubmission) {
rejectNextSubmission = false;
submissionInjection.definiteRejections += 1;
throw new CodingConversationServiceError(
503,
'CODING_MODEL_UNAVAILABLE',
'Controlled packaged submission rejection',
);
}
return await originalAcceptPrompt(promptInput);
};
proxyCompositionRun = {
composition: input.composition,
provider,
projectId,
projectPath: input.projectPath,
hostToken,
originalAcceptPrompt,
submissionInjection,
};
return {
projectId,