fix(pi): recover from Works prompt rejection
This commit is contained in:
@@ -36,7 +36,7 @@ import { PiProcessBudget, PiWorkerPool, type PiWorkerPoolEvent } from './worker-
|
||||
import { PiProjectWriteLeaseCoordinator, type PiProjectWriteLease } from './write-lease';
|
||||
|
||||
type ProofWorkerRole = 'parent' | 'child';
|
||||
type ProofProviderMode = 'subagent' | 'pressure' | 'resilience';
|
||||
type ProofProviderMode = 'subagent' | 'pressure' | 'resilience' | 'submit-rejection';
|
||||
type ProofMilestone = PiRuntimeTelemetryEvent['milestone'] | 'agent.start' | 'provider.first_event';
|
||||
type ProofMilestoneSource = 'main.telemetry' | 'pi.agent_start' | 'pi.assistant_message_start';
|
||||
|
||||
@@ -56,6 +56,8 @@ type ProviderRequest = {
|
||||
role: ProofWorkerRole;
|
||||
toolNames: string[];
|
||||
hasToolResult: boolean;
|
||||
firstMessageRole: string | null;
|
||||
outcome: 'accepted' | 'controlled_rejection' | 'developer_role_rejection';
|
||||
};
|
||||
|
||||
type HeldProviderResponse = {
|
||||
@@ -66,6 +68,7 @@ type HeldProviderResponse = {
|
||||
type LocalProofProvider = {
|
||||
baseUrl: string;
|
||||
requests: ProviderRequest[];
|
||||
roleContract: { developerRejected: boolean; systemAccepted: boolean } | null;
|
||||
activeCounts(): { parent: number; child: number };
|
||||
releaseChildren(): void;
|
||||
releaseParents(): void;
|
||||
@@ -184,6 +187,13 @@ export interface PiReleaseProxyCompositionStatus {
|
||||
logs: boolean;
|
||||
diagnostics: boolean;
|
||||
};
|
||||
providerCompatibility: {
|
||||
roleContract: { developerRejected: boolean; systemAccepted: boolean };
|
||||
requestRoles: string[];
|
||||
developerRoleRequests: number;
|
||||
systemRoleRequests: number;
|
||||
controlledDefiniteRejections: number;
|
||||
};
|
||||
realTurnVerified: false;
|
||||
}
|
||||
|
||||
@@ -196,6 +206,8 @@ export interface PiReleaseProxyCompositionProof extends PiReleaseProxyCompositio
|
||||
runStatus: string;
|
||||
userInputProjected: boolean;
|
||||
parentResponseProjected: boolean;
|
||||
definiteRejectionObserved: boolean;
|
||||
retryAccepted: boolean;
|
||||
};
|
||||
currentHostTokenUsedBy: ProofWorkerRole[];
|
||||
released: { workers: number };
|
||||
@@ -247,6 +259,7 @@ export interface PiReleaseResilienceProof extends PiReleaseResilienceStatus {
|
||||
const PROOF_ACCOUNT_ID = 'release-proof-account';
|
||||
const PROOF_AGENT_ID = 'release-proof-agent';
|
||||
const PROOF_MODEL_ID = 'release-proof-model';
|
||||
const PROXY_PROOF_MODEL_ID = 'deepseek-v4-pro';
|
||||
const PROOF_PROVIDER_FIRST_EVENT_DELAY_MS = 75;
|
||||
const EXPECTED_TURN_MILESTONES: readonly ProofMilestone[] = [
|
||||
'worker.queue_wait',
|
||||
@@ -327,6 +340,19 @@ function hasToolResult(body: Record<string, unknown>): boolean {
|
||||
));
|
||||
}
|
||||
|
||||
function firstMessageRole(body: Record<string, unknown>): string | null {
|
||||
if (!Array.isArray(body.messages)) return null;
|
||||
const first = body.messages[0];
|
||||
if (!first || typeof first !== 'object' || Array.isArray(first)) return null;
|
||||
const role = (first as { role?: unknown }).role;
|
||||
return typeof role === 'string' ? role : null;
|
||||
}
|
||||
|
||||
function rejectProofRequest(response: ServerResponse, message: string): void {
|
||||
response.writeHead(400, { 'content-type': 'application/json' });
|
||||
response.end(JSON.stringify({ error: { message } }));
|
||||
}
|
||||
|
||||
function writeChunk(
|
||||
response: ServerResponse,
|
||||
model: string,
|
||||
@@ -422,6 +448,7 @@ 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 {
|
||||
@@ -431,10 +458,43 @@ async function startLocalProofProvider(mode: ProofProviderMode): Promise<LocalPr
|
||||
}
|
||||
const body = await readRequestBody(request);
|
||||
const model = typeof body.model === 'string' ? body.model : PROOF_MODEL_ID;
|
||||
const leadingRole = firstMessageRole(body);
|
||||
if (request.headers['x-makelore-release-proof-role-canary'] === '1') {
|
||||
if (leadingRole === 'developer') {
|
||||
rejectProofRequest(response, 'developer role is unsupported by the controlled endpoint');
|
||||
} else if (leadingRole === 'system') {
|
||||
response.writeHead(204).end();
|
||||
} else {
|
||||
rejectProofRequest(response, 'role canary requires a leading system or developer message');
|
||||
}
|
||||
return;
|
||||
}
|
||||
const toolNames = toolNamesFrom(body);
|
||||
const role: ProofWorkerRole = toolNames.includes('subagent') ? 'parent' : 'child';
|
||||
const toolResult = hasToolResult(body);
|
||||
requests.push({ role, toolNames, hasToolResult: toolResult });
|
||||
const providerRequest: ProviderRequest = {
|
||||
role,
|
||||
toolNames,
|
||||
hasToolResult: toolResult,
|
||||
firstMessageRole: leadingRole,
|
||||
outcome: 'accepted',
|
||||
};
|
||||
requests.push(providerRequest);
|
||||
|
||||
if (mode === 'submit-rejection' && leadingRole === 'developer') {
|
||||
providerRequest.outcome = 'developer_role_rejection';
|
||||
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') {
|
||||
@@ -470,12 +530,12 @@ async function startLocalProofProvider(mode: ProofProviderMode): Promise<LocalPr
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode === 'subagent' && role === 'parent' && !toolResult) {
|
||||
if ((mode === 'subagent' || mode === 'submit-rejection') && role === 'parent' && !toolResult) {
|
||||
await delay(PROOF_PROVIDER_FIRST_EVENT_DELAY_MS);
|
||||
respondWithSubagentCall(response, model);
|
||||
return;
|
||||
}
|
||||
if (mode === 'subagent' && role === 'parent') {
|
||||
if ((mode === 'subagent' || mode === 'submit-rejection') && role === 'parent') {
|
||||
await delay(PROOF_PROVIDER_FIRST_EVENT_DELAY_MS);
|
||||
respondWithText(response, model, 'REAL_PARENT_COMPLETE');
|
||||
return;
|
||||
@@ -506,6 +566,33 @@ async function startLocalProofProvider(mode: ProofProviderMode): Promise<LocalPr
|
||||
if (!address || typeof address === 'string') {
|
||||
throw new Error('Release proof Provider did not bind a loopback port');
|
||||
}
|
||||
const baseUrl = `http://127.0.0.1:${address.port}/v1`;
|
||||
let roleContract: LocalProofProvider['roleContract'] = null;
|
||||
if (mode === 'submit-rejection') {
|
||||
const probe = async (role: 'developer' | 'system'): Promise<number> => {
|
||||
const response = await fetch(`${baseUrl}/chat/completions`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
'x-makelore-release-proof-role-canary': '1',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: PROXY_PROOF_MODEL_ID,
|
||||
messages: [{ role, content: 'role canary' }],
|
||||
}),
|
||||
});
|
||||
await response.text();
|
||||
return response.status;
|
||||
};
|
||||
const [developerStatus, systemStatus] = await Promise.all([
|
||||
probe('developer'),
|
||||
probe('system'),
|
||||
]);
|
||||
roleContract = {
|
||||
developerRejected: developerStatus === 400,
|
||||
systemAccepted: systemStatus === 204,
|
||||
};
|
||||
}
|
||||
const release = (role?: ProofWorkerRole) => {
|
||||
for (const entry of [...held]) {
|
||||
if (role && entry.role !== role) continue;
|
||||
@@ -514,8 +601,9 @@ async function startLocalProofProvider(mode: ProofProviderMode): Promise<LocalPr
|
||||
}
|
||||
};
|
||||
return {
|
||||
baseUrl: `http://127.0.0.1:${address.port}/v1`,
|
||||
baseUrl,
|
||||
requests,
|
||||
roleContract,
|
||||
activeCounts: () => ({
|
||||
parent: [...held].filter(({ role }) => role === 'parent').length,
|
||||
child: [...held].filter(({ role }) => role === 'child').length,
|
||||
@@ -782,13 +870,14 @@ async function inspectWindowsPiProcesses(hostToken: string): Promise<{
|
||||
return { supported: true, processes };
|
||||
}
|
||||
|
||||
function proxyProviderAccount(baseUrl: string): ProviderAccount {
|
||||
function proxyProviderAccount(baseUrl: string, modelId = PROOF_MODEL_ID): ProviderAccount {
|
||||
return {
|
||||
...providerAccount(baseUrl),
|
||||
model: modelId,
|
||||
label: 'Authenticated Host proxy release proof',
|
||||
metadata: {
|
||||
worksSquareCredentialMode: 'works_square_ai_gateway_proxy',
|
||||
customModels: [PROOF_MODEL_ID],
|
||||
customModels: [modelId],
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -811,6 +900,24 @@ async function proxyCompositionStatus(
|
||||
directoryContainsValue(logsDirectory, run.hostToken),
|
||||
]);
|
||||
const providerRequests = providerRequestCounts(run.provider);
|
||||
const providerCompatibility = {
|
||||
roleContract: run.provider.roleContract ?? {
|
||||
developerRejected: false,
|
||||
systemAccepted: false,
|
||||
},
|
||||
requestRoles: run.provider.requests.flatMap(({ firstMessageRole }) => (
|
||||
firstMessageRole ? [firstMessageRole] : []
|
||||
)),
|
||||
developerRoleRequests: run.provider.requests.filter(({ firstMessageRole }) => (
|
||||
firstMessageRole === 'developer'
|
||||
)).length,
|
||||
systemRoleRequests: run.provider.requests.filter(({ firstMessageRole }) => (
|
||||
firstMessageRole === 'system'
|
||||
)).length,
|
||||
controlledDefiniteRejections: run.provider.requests.filter(({ outcome }) => (
|
||||
outcome === 'controlled_rejection'
|
||||
)).length,
|
||||
};
|
||||
const parent = processInspection.processes
|
||||
.filter(({ role }) => role === 'parent')
|
||||
.map(({ processId }) => processId)
|
||||
@@ -832,6 +939,7 @@ async function proxyCompositionStatus(
|
||||
logs: !logsContainToken,
|
||||
diagnostics: !JSON.stringify(diagnostics).includes(run.hostToken),
|
||||
},
|
||||
providerCompatibility,
|
||||
realTurnVerified: false,
|
||||
};
|
||||
}
|
||||
@@ -1256,7 +1364,7 @@ export async function startFinalAsarProxyCompositionProof(input: {
|
||||
if (proxyCompositionRun) throw new Error('PI proxy composition proof is already running');
|
||||
const hostToken = input.hostToken.trim();
|
||||
if (!hostToken) throw new Error('Current Main Host token is unavailable');
|
||||
const provider = await startLocalProofProvider('subagent');
|
||||
const provider = await startLocalProofProvider('submit-rejection');
|
||||
let projectId: string | null = null;
|
||||
try {
|
||||
seedWorksSquareAIGatewayCredential({
|
||||
@@ -1264,7 +1372,10 @@ export async function startFinalAsarProxyCompositionProof(input: {
|
||||
oneApiBaseUrl: provider.baseUrl,
|
||||
});
|
||||
const providerService = getProviderService();
|
||||
await providerService.createAccount(proxyProviderAccount(input.hostProxyBaseUrl));
|
||||
await providerService.createAccount(proxyProviderAccount(
|
||||
input.hostProxyBaseUrl,
|
||||
PROXY_PROOF_MODEL_ID,
|
||||
));
|
||||
await providerService.setDefaultAccount(PROOF_ACCOUNT_ID);
|
||||
const project = await input.composition.projects.createProject({ projectPath: input.projectPath });
|
||||
projectId = project.project.id;
|
||||
@@ -1275,8 +1386,8 @@ export async function startFinalAsarProxyCompositionProof(input: {
|
||||
name: 'Packaged proxy proof agent',
|
||||
model: {
|
||||
accountId: PROOF_ACCOUNT_ID,
|
||||
modelId: PROOF_MODEL_ID,
|
||||
thinkingLevel: 'medium',
|
||||
modelId: PROXY_PROOF_MODEL_ID,
|
||||
thinkingLevel: 'high',
|
||||
},
|
||||
modelResolution: 'resolved',
|
||||
responsibility: {
|
||||
@@ -1366,6 +1477,15 @@ export async function finishFinalAsarProxyCompositionProof(): Promise<PiReleaseP
|
||||
if (!binding?.piSessionId || !binding.sessionKey) {
|
||||
throw new Error('First proxy Conversation did not persist its Pi session binding');
|
||||
}
|
||||
if (!finalStatus.providerCompatibility.roleContract.developerRejected
|
||||
|| !finalStatus.providerCompatibility.roleContract.systemAccepted
|
||||
|| finalStatus.providerCompatibility.developerRoleRequests !== 0
|
||||
|| finalStatus.providerCompatibility.systemRoleRequests < 2
|
||||
|| finalStatus.providerCompatibility.controlledDefiniteRejections !== 1) {
|
||||
throw new Error(
|
||||
`Works Provider role compatibility proof failed: ${JSON.stringify(finalStatus.providerCompatibility)}`,
|
||||
);
|
||||
}
|
||||
const tokenSafety = {
|
||||
argv: activeStatus.tokenSafety.argv && finalStatus.tokenSafety.argv,
|
||||
modelsJson: activeStatus.tokenSafety.modelsJson && finalStatus.tokenSafety.modelsJson,
|
||||
@@ -1387,8 +1507,10 @@ export async function finishFinalAsarProxyCompositionProof(): Promise<PiReleaseP
|
||||
bindingEstablished: true,
|
||||
workerStatus: snapshot.worker.status,
|
||||
runStatus: snapshot.run.status,
|
||||
userInputProjected: serializedNodes.includes('Exercise the packaged Main proxy Conversation'),
|
||||
userInputProjected: serializedNodes.includes('Retry the packaged Main proxy Conversation'),
|
||||
parentResponseProjected: serializedNodes.includes('REAL_PARENT_COMPLETE'),
|
||||
definiteRejectionObserved: finalStatus.providerCompatibility.controlledDefiniteRejections === 1,
|
||||
retryAccepted: finalStatus.providerCompatibility.systemRoleRequests >= 2,
|
||||
},
|
||||
currentHostTokenUsedBy,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user