427a8c151a
新增终端 AppData 配置向导并强化跨平台密钥与进程树安全 升级自主构建为十六任务产物 DAG 并接入两类真实画布素材 完善完成基线、并发补验、验证证据与受限画布返工合同 强化确定性与真实 Swarm E2E 的 exactly-once 和正式产物校验 修复确定性 Provider 终态修复后的复验交付状态 同步更新客户端实现计划、决策记录和踩坑说明
1336 lines
48 KiB
JavaScript
1336 lines
48 KiB
JavaScript
import { spawn } from 'node:child_process';
|
||
import { createHash, randomUUID } from 'node:crypto';
|
||
import fs from 'node:fs/promises';
|
||
import os from 'node:os';
|
||
import path from 'node:path';
|
||
import { fileURLToPath } from 'node:url';
|
||
|
||
import {
|
||
createDeterministicLaneDefenseRouter,
|
||
deterministicLaneDefenseInitialHtml,
|
||
deterministicLaneDefenseModel,
|
||
deterministicManifestReadyAgentIds,
|
||
hiddenCanvasCss,
|
||
startDeterministicLaneDefenseProvider,
|
||
visibleCanvasCss,
|
||
} from './deterministic-lane-defense-provider.mjs';
|
||
import { withLoopbackNoProxy } from './llm-transient-fault-proxy.mjs';
|
||
|
||
const appRoot = path.resolve(fileURLToPath(new URL('..', import.meta.url)));
|
||
const repoRoot = path.resolve(appRoot, '../..');
|
||
const realE2eScript = path.join(appRoot, 'scripts/agent-runtime-real-e2e.mjs');
|
||
const suite = 'supervisor-autonomous-playable-lane-defense';
|
||
const wrapperSuite =
|
||
'supervisor-autonomous-playable-lane-defense-deterministic';
|
||
const configFileName = 'game-creator.config.json';
|
||
const configSentinelName = '.deterministic-provider-e2e.json';
|
||
const configSentinelSchema = 'genarrative-deterministic-provider-e2e-config.v1';
|
||
const outputLimit = 32 * 1024 * 1024;
|
||
|
||
function hashValue(value) {
|
||
return createHash('sha256').update(value).digest('hex');
|
||
}
|
||
|
||
function assert(condition, code) {
|
||
if (condition) return;
|
||
const error = new Error(code);
|
||
error.code = code;
|
||
throw error;
|
||
}
|
||
|
||
function parseArguments(args) {
|
||
let keepProject = false;
|
||
let selfTest = false;
|
||
for (const arg of args) {
|
||
if (arg === '--keep-project') keepProject = true;
|
||
else if (arg === '--self-test') selfTest = true;
|
||
else throw new Error('unknown-argument');
|
||
}
|
||
assert(!(keepProject && selfTest), 'self-test-keep-project-conflict');
|
||
return { keepProject, selfTest };
|
||
}
|
||
|
||
function appendBounded(current, chunk) {
|
||
const combined = Buffer.concat([current, chunk]);
|
||
if (combined.length > outputLimit) throw new Error('child-output-too-large');
|
||
return combined;
|
||
}
|
||
|
||
function runChild(args, environment) {
|
||
return new Promise((resolve) => {
|
||
const child = spawn(process.execPath, args, {
|
||
cwd: appRoot,
|
||
env: environment,
|
||
stdio: ['ignore', 'pipe', 'pipe'],
|
||
});
|
||
let stdout = Buffer.alloc(0);
|
||
let stderr = Buffer.alloc(0);
|
||
let outputError = null;
|
||
child.stdout.on('data', (chunk) => {
|
||
try {
|
||
stdout = appendBounded(stdout, chunk);
|
||
} catch (error) {
|
||
outputError = error;
|
||
child.kill('SIGTERM');
|
||
}
|
||
});
|
||
child.stderr.on('data', (chunk) => {
|
||
try {
|
||
stderr = appendBounded(stderr, chunk);
|
||
} catch (error) {
|
||
outputError = error;
|
||
child.kill('SIGTERM');
|
||
}
|
||
});
|
||
child.once('error', (error) =>
|
||
resolve({ code: null, signal: null, error, stdout, stderr }),
|
||
);
|
||
child.once('close', (code, signal) =>
|
||
resolve({ code, signal, error: outputError, stdout, stderr }),
|
||
);
|
||
});
|
||
}
|
||
|
||
function safeChildDiagnostic(result) {
|
||
return {
|
||
exitCode: Number.isInteger(result.code) ? result.code : null,
|
||
signal: typeof result.signal === 'string' ? result.signal : null,
|
||
errorCode:
|
||
typeof result.error?.code === 'string' ? result.error.code : null,
|
||
stdoutBytes: result.stdout.length,
|
||
stdoutSha256: hashValue(result.stdout),
|
||
stderrBytes: result.stderr.length,
|
||
stderrSha256: hashValue(result.stderr),
|
||
};
|
||
}
|
||
|
||
function parseChildReport(result) {
|
||
try {
|
||
const report = JSON.parse(result.stdout.toString('utf8'));
|
||
assert(report && typeof report === 'object', 'child-report-root-invalid');
|
||
return report;
|
||
} catch (error) {
|
||
if (error?.code) throw error;
|
||
const wrapped = new Error('child-report-json-invalid');
|
||
wrapped.code = 'child-report-json-invalid';
|
||
throw wrapped;
|
||
}
|
||
}
|
||
|
||
function responsibilityContractEvidence(stats) {
|
||
const qualityPlanningCount =
|
||
stats?.byAgent?.['quality-review']?.planning ?? null;
|
||
const evidence = {
|
||
delegationContractCount: stats?.delegationContractCount ?? null,
|
||
delegationContractViolationCount:
|
||
stats?.delegationContractViolationCount ?? null,
|
||
codePrototypeDelegationCount: stats?.codePrototypeDelegationCount ?? null,
|
||
codePrototypeExpectedArtifactCount:
|
||
stats?.codePrototypeExpectedArtifactCount ?? null,
|
||
codePrototypeGameIndexArtifactDelegationCount:
|
||
stats?.codePrototypeGameIndexArtifactDelegationCount ?? null,
|
||
codePrototypeProjectMutationCount:
|
||
stats?.byAgent?.['code-prototype']?.projectMutation ?? null,
|
||
qualityReviewDelegationCount: stats?.qualityReviewDelegationCount ?? null,
|
||
qualityReviewReadOnlyDelegationCount:
|
||
stats?.qualityReviewReadOnlyDelegationCount ?? null,
|
||
qualityReviewExpectedArtifactCount:
|
||
stats?.qualityReviewExpectedArtifactCount ?? null,
|
||
artAssetPlanDelegationCount: stats?.artAssetPlanDelegationCount ?? null,
|
||
artAssetPlanExpectedArtifactCount:
|
||
stats?.artAssetPlanExpectedArtifactCount ?? null,
|
||
artAssetPlanArtifactContractCount:
|
||
stats?.artAssetPlanArtifactContractCount ?? null,
|
||
qualityReviewProjectMutationCount:
|
||
stats?.byAgent?.['quality-review']?.projectMutation ?? null,
|
||
qualityPlanningCount,
|
||
qualityRevisionReplanCount: stats?.qualityRevisionReplanCount ?? null,
|
||
};
|
||
return {
|
||
...evidence,
|
||
codeOwnsGameIndex:
|
||
Number.isInteger(evidence.codePrototypeDelegationCount) &&
|
||
evidence.codePrototypeDelegationCount >= 1 &&
|
||
evidence.codePrototypeExpectedArtifactCount ===
|
||
evidence.codePrototypeDelegationCount &&
|
||
evidence.codePrototypeGameIndexArtifactDelegationCount ===
|
||
evidence.codePrototypeDelegationCount &&
|
||
Number.isInteger(evidence.codePrototypeProjectMutationCount) &&
|
||
evidence.codePrototypeProjectMutationCount >= 2,
|
||
qualityIsReadOnly:
|
||
evidence.qualityReviewDelegationCount === 1 &&
|
||
evidence.qualityReviewReadOnlyDelegationCount === 1 &&
|
||
evidence.qualityReviewExpectedArtifactCount === 0 &&
|
||
evidence.qualityReviewProjectMutationCount === 0,
|
||
qualityIndependentOfCodeTiming:
|
||
Number.isInteger(qualityPlanningCount) &&
|
||
qualityPlanningCount >= 1 &&
|
||
Number.isInteger(evidence.qualityRevisionReplanCount) &&
|
||
evidence.qualityRevisionReplanCount >= 0 &&
|
||
evidence.qualityRevisionReplanCount <= 1,
|
||
artOwnsGeneratedAsset:
|
||
evidence.artAssetPlanDelegationCount === 1 &&
|
||
evidence.artAssetPlanExpectedArtifactCount === 2 &&
|
||
evidence.artAssetPlanArtifactContractCount === 1,
|
||
contractViolationFree:
|
||
Number.isInteger(evidence.delegationContractCount) &&
|
||
evidence.delegationContractCount >= 3 &&
|
||
evidence.delegationContractViolationCount === 0,
|
||
};
|
||
}
|
||
|
||
function expectedProviderStats(stats) {
|
||
const responsibility = responsibilityContractEvidence(stats);
|
||
const manifestReadyTasks = manifestReadyTaskEvidence(stats);
|
||
return (
|
||
Number.isInteger(stats.requestCount) &&
|
||
stats.requestCount >= 40 &&
|
||
stats.planningRequestCount +
|
||
stats.finalReplyRequestCount +
|
||
(stats.contextCompactionRequestCount ?? 0) +
|
||
(stats.imageInspectionRequestCount ?? 0) ===
|
||
stats.requestCount &&
|
||
stats.finalReplyRequestCount === 0 &&
|
||
stats.initialDelegationCount === 3 &&
|
||
stats.followupDelegationCount === 0 &&
|
||
stats.runStatusCount >= 1 &&
|
||
stats.sourceWriteCount >= 7 &&
|
||
stats.staticSmokeCount >= 4 &&
|
||
stats.previewValidationCount >= 2 &&
|
||
stats.supervisorDirectMutationAttemptCount === 0 &&
|
||
manifestReadyTasks.exactlyOnce &&
|
||
stats.manifestReadyTaskFileWriteCount >= 7 &&
|
||
stats.manifestReadyTaskPreviewValidationCount >= 1 &&
|
||
stats.manifestReadyTaskCanvasGenerationCount >= 1 &&
|
||
stats.manifestReadyTaskCanvasGenerationCount <= 2 &&
|
||
stats.imageInspectionRequestCount === 1 &&
|
||
stats.canvasGenerationRequestCount === 2 &&
|
||
stats.canvasDownloadRequestCount === 2 &&
|
||
stats.canvasGeneratedAspectRatios?.['16:9'] === 1 &&
|
||
stats.canvasGeneratedAspectRatios?.['1:1'] === 1 &&
|
||
stats.canonicalCodeRunCount === 1 &&
|
||
stats.unexpectedRequestCount === 0 &&
|
||
Object.keys(stats.rejectionCodes ?? {}).length === 0 &&
|
||
stats.byAgent?.['project-supervisor']?.planning >= 5 &&
|
||
stats.byAgent?.['project-supervisor']?.finalReply === 0 &&
|
||
stats.byAgent?.['project-supervisor']?.projectMutation === 0 &&
|
||
stats.byAgent?.['code-prototype']?.planning >= 6 &&
|
||
stats.byAgent?.['code-prototype']?.finalReply === 0 &&
|
||
stats.byAgent?.['code-prototype']?.projectMutation >= 2 &&
|
||
stats.byAgent?.['quality-review']?.finalReply === 0 &&
|
||
responsibility.codeOwnsGameIndex &&
|
||
responsibility.qualityIsReadOnly &&
|
||
responsibility.qualityIndependentOfCodeTiming &&
|
||
responsibility.artOwnsGeneratedAsset &&
|
||
responsibility.contractViolationFree
|
||
);
|
||
}
|
||
|
||
function manifestReadyTaskEvidence(stats) {
|
||
const rawCounts = stats?.readyTaskCountsByAgent;
|
||
const byAgent =
|
||
rawCounts && typeof rawCounts === 'object' && !Array.isArray(rawCounts)
|
||
? Object.fromEntries(
|
||
Object.entries(rawCounts)
|
||
.sort(([left], [right]) => left.localeCompare(right))
|
||
.map(([agentId, counts]) => [
|
||
agentId,
|
||
{
|
||
run: counts?.run ?? null,
|
||
completion: counts?.completion ?? null,
|
||
},
|
||
]),
|
||
)
|
||
: {};
|
||
const actualAgentIds = Object.keys(byAgent);
|
||
const expectedAgentIds = [...deterministicManifestReadyAgentIds].sort();
|
||
const exactAgentSet =
|
||
actualAgentIds.length === expectedAgentIds.length &&
|
||
actualAgentIds.every(
|
||
(agentId, index) => agentId === expectedAgentIds[index],
|
||
);
|
||
const runTotal = Object.values(byAgent).reduce(
|
||
(total, counts) => total + (Number.isInteger(counts.run) ? counts.run : 0),
|
||
0,
|
||
);
|
||
const completionTotal = Object.values(byAgent).reduce(
|
||
(total, counts) =>
|
||
total + (Number.isInteger(counts.completion) ? counts.completion : 0),
|
||
0,
|
||
);
|
||
const perAgentExactlyOnce =
|
||
exactAgentSet &&
|
||
expectedAgentIds.every(
|
||
(agentId) =>
|
||
byAgent[agentId]?.run === 1 && byAgent[agentId]?.completion === 1,
|
||
);
|
||
return {
|
||
expectedAgentIds,
|
||
byAgent,
|
||
runTotal,
|
||
completionTotal,
|
||
exactAgentSet,
|
||
perAgentExactlyOnce,
|
||
exactlyOnce:
|
||
perAgentExactlyOnce &&
|
||
runTotal === deterministicManifestReadyAgentIds.length &&
|
||
completionTotal === deterministicManifestReadyAgentIds.length &&
|
||
stats?.manifestReadyTaskRunCount ===
|
||
deterministicManifestReadyAgentIds.length &&
|
||
stats?.manifestReadyTaskCompletionCount ===
|
||
deterministicManifestReadyAgentIds.length,
|
||
};
|
||
}
|
||
|
||
const requiredZeroChildEvidenceFields = Object.freeze([
|
||
'activeRunnerKillCount',
|
||
'approveInputCount',
|
||
'answerInputCount',
|
||
'steerInputCount',
|
||
'turnReportWaitingForConfirmationCount',
|
||
'turnReportWaitingForUserInputCount',
|
||
'turnReportReconciliationAgentCount',
|
||
'providerLifecycleFailedCount',
|
||
'openProviderLifecycleCount',
|
||
'pendingActionCount',
|
||
'confirmationSidecarCount',
|
||
'userInputSidecarCount',
|
||
'providerActionBatchSidecarCount',
|
||
'providerRetrySidecarCount',
|
||
'providerHandoffSidecarCount',
|
||
'toolPlanHandoffSidecarCount',
|
||
'finalizationJournalCount',
|
||
'reconciliationResidueCount',
|
||
]);
|
||
|
||
function expectedChildReport(report, options, providerStats) {
|
||
const evidence = report?.evidence;
|
||
const providerRequestCount = providerStats?.requestCount;
|
||
const runtimeProviderRequestCount =
|
||
Number.isInteger(providerRequestCount) &&
|
||
Number.isInteger(providerStats?.interactionExecuteCount) &&
|
||
Number.isInteger(providerStats?.imageInspectionRequestCount)
|
||
? providerRequestCount -
|
||
providerStats.interactionExecuteCount -
|
||
providerStats.imageInspectionRequestCount
|
||
: null;
|
||
return (
|
||
report?.status === 'PASS' &&
|
||
report?.suite === suite &&
|
||
report?.errorCount === 0 &&
|
||
Array.isArray(report?.blocked) &&
|
||
report.blocked.length === 0 &&
|
||
report?.cleanup?.performed === !options.keepProject &&
|
||
report?.cleanup?.kept === options.keepProject &&
|
||
evidence?.evidenceCompleteness === 'complete' &&
|
||
evidence?.dedicatedZeroInterventionPath === true &&
|
||
evidence?.stdinTaskCount === 1 &&
|
||
evidence?.stdinEndedAfterTask === true &&
|
||
evidence?.turnReportOutcome === 'settled' &&
|
||
evidence?.parentTaskStatus === 'completed' &&
|
||
evidence?.parentRuntimeStatus === 'idle' &&
|
||
evidence?.parentRuntimePhase === 'completed' &&
|
||
evidence?.laneDefensePlaytestPassed === true &&
|
||
evidence?.laneDefenseAssertionCount === 37 &&
|
||
evidence?.laneDefensePassedAssertionCount === 37 &&
|
||
evidence?.browserValidationPassed === true &&
|
||
evidence?.staticSmokePassed === true &&
|
||
evidence?.gameIndexChanged === true &&
|
||
Number.isInteger(evidence?.projectRevisionDelta) &&
|
||
evidence.projectRevisionDelta > 0 &&
|
||
evidence?.finalSupervisorAssistantCount === 1 &&
|
||
evidence?.professionalAssistantCount >= 3 &&
|
||
evidence?.providerRequestIdentityCount === runtimeProviderRequestCount &&
|
||
evidence?.providerLifecycleStartedCount === runtimeProviderRequestCount &&
|
||
evidence?.providerLifecycleTerminalCount === runtimeProviderRequestCount &&
|
||
evidence?.providerLifecycleCompletedCount === runtimeProviderRequestCount &&
|
||
requiredZeroChildEvidenceFields.every((field) => evidence?.[field] === 0)
|
||
);
|
||
}
|
||
|
||
function syntheticPayload(agentId, runId, tools, extraContext = '') {
|
||
return {
|
||
model: deterministicLaneDefenseModel,
|
||
stream: false,
|
||
messages: [
|
||
{
|
||
role: 'user',
|
||
content: `- templateAgentId: ${agentId}\n- runId: ${runId}\n${extraContext}`,
|
||
},
|
||
],
|
||
tools: tools.map((name) => ({ type: 'function', function: { name } })),
|
||
};
|
||
}
|
||
|
||
function responseFunctionNames(response) {
|
||
return response.choices[0].message.tool_calls.map(
|
||
(call) => call.function.name,
|
||
);
|
||
}
|
||
|
||
function responseFunctionCalls(response) {
|
||
return response.choices[0].message.tool_calls.map((call) => ({
|
||
name: call.function.name,
|
||
arguments: JSON.parse(call.function.arguments),
|
||
}));
|
||
}
|
||
|
||
async function runSelfTest() {
|
||
const html = deterministicLaneDefenseInitialHtml();
|
||
assert([...html].length <= 8_000, 'self-test-html-source-budget-invalid');
|
||
assert(
|
||
html.includes(hiddenCanvasCss) &&
|
||
!html.includes(visibleCanvasCss) &&
|
||
html.includes('playable-web-game-state.v1') &&
|
||
html.includes('data-playtest-id="next-level"') &&
|
||
html.includes('Goal: defend the garden and win every wave.') &&
|
||
html.includes('requestAnimationFrame'),
|
||
'self-test-html-contract-invalid',
|
||
);
|
||
const apiKey = `deterministic-self-test-${randomUUID()}`;
|
||
const router = createDeterministicLaneDefenseRouter({ apiKey });
|
||
const allTools = [
|
||
'update_agent_plan',
|
||
'respond_to_user',
|
||
'runtime_tool_agent_delegate',
|
||
'runtime_tool_agent_run_status',
|
||
'runtime_tool_command_run_limited',
|
||
'runtime_tool_file_patch',
|
||
'runtime_tool_file_write',
|
||
'runtime_tool_preview_validate',
|
||
'runtime_tool_task_list',
|
||
];
|
||
const manifestReadyTools = [
|
||
...allTools,
|
||
'runtime_tool_asset_list',
|
||
'runtime_tool_file_read',
|
||
];
|
||
const route = (agentId, runId, tools = allTools, extraContext = '') =>
|
||
router.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(agentId, runId, tools, extraContext),
|
||
});
|
||
const initialDelegationResponse = route('project-supervisor', 'parent-run');
|
||
const initialDelegationCalls = responseFunctionCalls(
|
||
initialDelegationResponse,
|
||
);
|
||
assert(
|
||
initialDelegationCalls.map((call) => call.name).join(',') ===
|
||
'runtime_tool_agent_delegate,runtime_tool_agent_delegate,runtime_tool_agent_delegate',
|
||
'self-test-initial-delegation-invalid',
|
||
);
|
||
const initialCodeDelegation = initialDelegationCalls[0]?.arguments?.input;
|
||
const initialQualityDelegation = initialDelegationCalls[1]?.arguments?.input;
|
||
const initialArtDelegation = initialDelegationCalls[2]?.arguments?.input;
|
||
assert(
|
||
initialCodeDelegation?.agentId === 'code-prototype' &&
|
||
JSON.stringify(initialCodeDelegation.expectedArtifacts) ===
|
||
JSON.stringify(['game/index.html']) &&
|
||
initialQualityDelegation?.agentId === 'quality-review' &&
|
||
initialQualityDelegation.task.includes('只读验收') &&
|
||
initialQualityDelegation.task.includes('不要修改任何文件') &&
|
||
initialQualityDelegation.task.includes(
|
||
'不要读取或依赖并行 code-prototype',
|
||
) &&
|
||
Array.isArray(initialQualityDelegation.expectedArtifacts) &&
|
||
initialQualityDelegation.expectedArtifacts.length === 0 &&
|
||
initialArtDelegation?.agentId === 'art-asset-plan' &&
|
||
initialArtDelegation.task.includes('canvas.asset_generate') &&
|
||
JSON.stringify(initialArtDelegation.expectedArtifacts) ===
|
||
JSON.stringify([
|
||
'assets/manifest.art.json',
|
||
'assets/art-spritesheet.png',
|
||
]),
|
||
'self-test-initial-delegation-contract-invalid',
|
||
);
|
||
const qualityPlan =
|
||
'计划进度:\n- #1 [in_progress] 核对完整玩法\n- #2 [pending] 回传结论\n工具策略:auto=无';
|
||
const builderPlan =
|
||
'计划进度:\n- #1 [completed] 生成入口\n- #2 [completed] 静态验证\n- #3 [in_progress] 回传结论\n工具策略:auto=无';
|
||
assert(
|
||
responseFunctionNames(
|
||
route('quality-review', 'quality-run', allTools, qualityPlan),
|
||
).join(',') === 'update_agent_plan,respond_to_user',
|
||
'self-test-quality-read-only-invalid',
|
||
);
|
||
route('code-prototype', 'initial-code-run');
|
||
route('code-prototype', 'initial-code-run');
|
||
assert(
|
||
responseFunctionNames(
|
||
route('code-prototype', 'initial-code-run', allTools, builderPlan),
|
||
).join(',') === 'update_agent_plan,respond_to_user',
|
||
'self-test-builder-plan-completion-invalid',
|
||
);
|
||
assert(
|
||
responseFunctionNames(
|
||
route(
|
||
'quality-review',
|
||
'quality-run',
|
||
allTools,
|
||
`${qualityPlan}\n已有工具观察:\n${JSON.stringify([
|
||
{
|
||
tool: 'runtime.verification',
|
||
status: 'blocked',
|
||
summary: '最终回复基于的项目 revision 已过期',
|
||
detail: 'responseRevision=0, currentRevision=1',
|
||
},
|
||
])}`,
|
||
),
|
||
).join(',') === 'update_agent_plan,respond_to_user',
|
||
'self-test-quality-stale-replan-invalid',
|
||
);
|
||
route('project-supervisor', 'parent-run');
|
||
route('project-supervisor', 'parent-run');
|
||
route('project-supervisor', 'parent-run');
|
||
assert(
|
||
responseFunctionNames(route('project-supervisor', 'parent-run'))[0] ===
|
||
'runtime_tool_file_patch',
|
||
'self-test-forbidden-parent-mutation-invalid',
|
||
);
|
||
const followupDelegationResponse = route(
|
||
'project-supervisor',
|
||
'parent-run',
|
||
['runtime_tool_agent_delegate'],
|
||
'当前父 run 已进入只编排模式,本次修复的原生工具目录只保留 agent.delegate',
|
||
);
|
||
const followupDelegationCall = responseFunctionCalls(
|
||
followupDelegationResponse,
|
||
)[0];
|
||
assert(
|
||
followupDelegationCall?.name === 'runtime_tool_agent_delegate' &&
|
||
followupDelegationCall.arguments?.input?.agentId === 'code-prototype' &&
|
||
JSON.stringify(
|
||
followupDelegationCall.arguments.input.expectedArtifacts,
|
||
) === JSON.stringify(['game/index.html']),
|
||
'self-test-followup-delegation-invalid',
|
||
);
|
||
route('code-prototype', 'repair-code-run');
|
||
route('code-prototype', 'repair-code-run');
|
||
route('code-prototype', 'repair-code-run', allTools, builderPlan);
|
||
assert(
|
||
responseFunctionNames(route('project-supervisor', 'parent-run'))[0] ===
|
||
'runtime_tool_agent_run_status',
|
||
'self-test-repair-claim-before-verification-invalid',
|
||
);
|
||
assert(
|
||
responseFunctionNames(route('project-supervisor', 'parent-run')).join(
|
||
',',
|
||
) === 'runtime_tool_task_list,runtime_tool_agent_run_status',
|
||
'self-test-manifest-wait-invalid',
|
||
);
|
||
assert(
|
||
responseFunctionNames(
|
||
route(
|
||
'project-supervisor',
|
||
'parent-run',
|
||
allTools,
|
||
'已有工具观察:\nseedTaskCounts: completed=16 running=0 pending=0 waiting=0 failed=0 total=16',
|
||
),
|
||
).join(',') ===
|
||
'runtime_tool_command_run_limited,runtime_tool_preview_validate',
|
||
'self-test-repair-verification-batch-invalid',
|
||
);
|
||
route('project-supervisor', 'parent-run');
|
||
|
||
for (const agentId of deterministicManifestReadyAgentIds) {
|
||
const runId = `manifest-ready-${agentId}`;
|
||
const extraContext =
|
||
`处理 manifest ready 任务:${agentId}\n` + `任务 ID:${agentId}`;
|
||
let terminalCompletionCount = 0;
|
||
for (let attempt = 0; attempt < 8; attempt += 1) {
|
||
const response = route(agentId, runId, manifestReadyTools, extraContext);
|
||
if (responseFunctionNames(response).includes('respond_to_user')) {
|
||
terminalCompletionCount += 1;
|
||
break;
|
||
}
|
||
}
|
||
assert(
|
||
terminalCompletionCount === 1,
|
||
`self-test-manifest-ready-terminal-invalid:${agentId}`,
|
||
);
|
||
}
|
||
const stats = router.getStats();
|
||
const manifestReadyTasks = manifestReadyTaskEvidence(stats);
|
||
assert(
|
||
manifestReadyTasks.exactlyOnce,
|
||
'self-test-manifest-ready-exactly-once-invalid',
|
||
);
|
||
|
||
const missingReadyTaskStats = structuredClone(stats);
|
||
const missingAgentId = deterministicManifestReadyAgentIds.at(-1);
|
||
delete missingReadyTaskStats.readyTaskCountsByAgent[missingAgentId];
|
||
missingReadyTaskStats.manifestReadyTaskRunCount -= 1;
|
||
missingReadyTaskStats.manifestReadyTaskCompletionCount -= 1;
|
||
assert(
|
||
!manifestReadyTaskEvidence(missingReadyTaskStats).exactlyOnce,
|
||
'self-test-manifest-ready-missing-accepted',
|
||
);
|
||
|
||
const duplicateReadyTaskStats = structuredClone(stats);
|
||
const duplicateAgentId = deterministicManifestReadyAgentIds[0];
|
||
duplicateReadyTaskStats.readyTaskCountsByAgent[duplicateAgentId].completion +=
|
||
1;
|
||
duplicateReadyTaskStats.manifestReadyTaskCompletionCount += 1;
|
||
assert(
|
||
!manifestReadyTaskEvidence(duplicateReadyTaskStats).exactlyOnce,
|
||
'self-test-manifest-ready-duplicate-accepted',
|
||
);
|
||
|
||
const retryObservationContext = (observations) =>
|
||
`\n已有工具观察:\n${JSON.stringify(observations)}`;
|
||
const staleObservation = {
|
||
tool: 'runtime.verification',
|
||
status: 'blocked',
|
||
summary: '最终回复生成期间项目 revision 已变化',
|
||
detail: 'responseRevision=1, currentRevision=2',
|
||
};
|
||
const successfulSmokeObservation = {
|
||
tool: 'command.run_limited',
|
||
status: 'ok',
|
||
summary: 'game.static_smoke 已完成',
|
||
detail: null,
|
||
};
|
||
const blockedSmokeObservation = {
|
||
tool: 'command.run_limited',
|
||
status: 'blocked',
|
||
summary: '仓库规范或启动上下文已漂移,旧动作未执行',
|
||
detail:
|
||
'repositoryContextDrift=true · 请在同一 run 下一轮 planning 重新确认适用规范',
|
||
};
|
||
const projectRevisionDriftSmokeObservation = {
|
||
tool: 'command.run_limited',
|
||
status: 'blocked',
|
||
summary: '并行项目变更使旧动作过期,旧动作未执行',
|
||
detail:
|
||
'projectRevisionDrift=true · expectedRevision=4 · currentRevision=5 · replanRequired=true',
|
||
};
|
||
const successfulProjectMutationObservation = {
|
||
tool: 'project.patchset',
|
||
status: 'ok',
|
||
summary: 'project.patchset 已原子应用 2 项变更',
|
||
detail: 'checkpointId=checkpoint-concurrent-ready-batch',
|
||
};
|
||
const captureProviderErrorCode = (operation) => {
|
||
try {
|
||
operation();
|
||
return null;
|
||
} catch (error) {
|
||
return error?.code ?? null;
|
||
}
|
||
};
|
||
|
||
const oldSmokeRouter = createDeterministicLaneDefenseRouter({ apiKey });
|
||
const oldSmokeRoute = (extraContext = '') =>
|
||
oldSmokeRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
'code-prototype',
|
||
'old-smoke-initial-code-run',
|
||
allTools,
|
||
extraContext,
|
||
),
|
||
});
|
||
oldSmokeRoute();
|
||
oldSmokeRoute();
|
||
oldSmokeRoute();
|
||
assert(
|
||
responseFunctionNames(
|
||
oldSmokeRoute(retryObservationContext([successfulSmokeObservation])),
|
||
).includes('respond_to_user') &&
|
||
captureProviderErrorCode(() =>
|
||
oldSmokeRoute(retryObservationContext([successfulSmokeObservation])),
|
||
) === 'provider-terminal-replay-unarmed:code-prototype',
|
||
'self-test-initial-terminal-old-smoke-replayed',
|
||
);
|
||
|
||
const readOnlyCommandRouter = createDeterministicLaneDefenseRouter({
|
||
apiKey,
|
||
});
|
||
const readOnlyCommandRunId = 'read-only-command-only-ready-run';
|
||
const readOnlyCommandRoute = (tools) =>
|
||
readOnlyCommandRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
'quality-review',
|
||
readOnlyCommandRunId,
|
||
tools,
|
||
'处理 manifest ready 任务:quality-review\n任务 ID:quality-review',
|
||
),
|
||
});
|
||
readOnlyCommandRoute(manifestReadyTools);
|
||
readOnlyCommandRoute(manifestReadyTools);
|
||
const readOnlyCommandCode = captureProviderErrorCode(() =>
|
||
readOnlyCommandRoute(['runtime_tool_command_run_limited']),
|
||
);
|
||
const readOnlyCommandStats = readOnlyCommandRouter.getStats();
|
||
assert(
|
||
readOnlyCommandCode ===
|
||
'provider-ready-finalization-tools-invalid:quality-review:runtime_tool_command_run_limited' &&
|
||
readOnlyCommandStats.readyTaskCountsByAgent['quality-review']
|
||
?.completion === 0 &&
|
||
readOnlyCommandStats.manifestReadyTaskStaticSmokeCount === 0,
|
||
'self-test-read-only-command-only-not-rejected',
|
||
);
|
||
|
||
const preCompletionRouter = createDeterministicLaneDefenseRouter({ apiKey });
|
||
const preCompletionAgentId = 'preview-readiness';
|
||
const preCompletionRunId = 'pre-completion-transient-ready-run';
|
||
const preCompletionRoute = (tools, extraContext = '') =>
|
||
preCompletionRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
preCompletionAgentId,
|
||
preCompletionRunId,
|
||
tools,
|
||
`处理 manifest ready 任务:${preCompletionAgentId}\n任务 ID:${preCompletionAgentId}${extraContext}`,
|
||
),
|
||
});
|
||
preCompletionRoute(manifestReadyTools);
|
||
for (let retry = 0; retry < 16; retry += 1) {
|
||
assert(
|
||
responseFunctionNames(
|
||
preCompletionRoute(
|
||
['runtime_tool_command_run_limited'],
|
||
retryObservationContext([blockedSmokeObservation]),
|
||
),
|
||
).join(',') === 'runtime_tool_command_run_limited',
|
||
`self-test-ready-precompletion-transient-invalid:${retry}`,
|
||
);
|
||
}
|
||
const preCompletionLimitCode = captureProviderErrorCode(() =>
|
||
preCompletionRoute(
|
||
['runtime_tool_command_run_limited'],
|
||
retryObservationContext([blockedSmokeObservation]),
|
||
),
|
||
);
|
||
const preCompletionStats = preCompletionRouter.getStats();
|
||
assert(
|
||
preCompletionLimitCode ===
|
||
`provider-ready-precompletion-verification-exhausted:${preCompletionAgentId}` &&
|
||
preCompletionStats.readyTaskCountsByAgent[preCompletionAgentId]
|
||
?.completion === 0 &&
|
||
preCompletionStats.manifestReadyTaskStaticSmokeCount === 17,
|
||
'self-test-ready-precompletion-limit-invalid',
|
||
);
|
||
|
||
const exerciseInitialTerminalRetryLimit = ({
|
||
agentId,
|
||
runId,
|
||
tools,
|
||
initialRequestCount,
|
||
}) => {
|
||
const terminalRouter = createDeterministicLaneDefenseRouter({ apiKey });
|
||
const terminalRoute = (extraContext = '') =>
|
||
terminalRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(agentId, runId, tools, extraContext),
|
||
});
|
||
for (let request = 0; request < initialRequestCount; request += 1) {
|
||
terminalRoute();
|
||
}
|
||
let deliveryCount = 0;
|
||
for (let retry = 0; retry < 16; retry += 1) {
|
||
assert(
|
||
responseFunctionNames(
|
||
terminalRoute(retryObservationContext([staleObservation])),
|
||
).join(',') === 'runtime_tool_command_run_limited',
|
||
`self-test-initial-terminal-verification-invalid:${agentId}:${retry}`,
|
||
);
|
||
if (
|
||
responseFunctionNames(
|
||
terminalRoute(retryObservationContext([successfulSmokeObservation])),
|
||
).includes('respond_to_user')
|
||
) {
|
||
deliveryCount += 1;
|
||
}
|
||
}
|
||
const limitCode = captureProviderErrorCode(() =>
|
||
terminalRoute(retryObservationContext([staleObservation])),
|
||
);
|
||
assert(
|
||
deliveryCount === 16 &&
|
||
limitCode === `provider-terminal-retry-exhausted:${agentId}`,
|
||
`self-test-initial-terminal-retry-limit-invalid:${agentId}`,
|
||
);
|
||
};
|
||
exerciseInitialTerminalRetryLimit({
|
||
agentId: 'code-prototype',
|
||
runId: 'initial-code-terminal-retry-run',
|
||
tools: allTools,
|
||
initialRequestCount: 3,
|
||
});
|
||
exerciseInitialTerminalRetryLimit({
|
||
agentId: 'art-asset-plan',
|
||
runId: 'initial-art-terminal-retry-run',
|
||
tools: [...manifestReadyTools, 'runtime_tool_canvas_asset_generate'],
|
||
initialRequestCount: 5,
|
||
});
|
||
|
||
const duplicateRouter = createDeterministicLaneDefenseRouter({ apiKey });
|
||
const duplicateRoute = (runId, extraContext = '') =>
|
||
duplicateRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
duplicateAgentId,
|
||
runId,
|
||
manifestReadyTools,
|
||
`处理 manifest ready 任务:${duplicateAgentId}\n任务 ID:${duplicateAgentId}${extraContext}`,
|
||
),
|
||
});
|
||
duplicateRoute('duplicate-ready-run');
|
||
duplicateRoute('duplicate-ready-run');
|
||
let duplicateTerminalCode = null;
|
||
try {
|
||
duplicateRoute('duplicate-ready-run');
|
||
} catch (error) {
|
||
duplicateTerminalCode = error?.code ?? null;
|
||
}
|
||
let duplicateRunCode = null;
|
||
try {
|
||
duplicateRoute('second-ready-run');
|
||
} catch (error) {
|
||
duplicateRunCode = error?.code ?? null;
|
||
}
|
||
assert(
|
||
duplicateTerminalCode ===
|
||
`provider-ready-run-terminal-duplicate:${duplicateAgentId}` &&
|
||
duplicateRunCode ===
|
||
`provider-ready-agent-run-duplicate:${duplicateAgentId}`,
|
||
'self-test-manifest-ready-provider-duplicate-not-rejected',
|
||
);
|
||
|
||
const retryAgentId = 'preview-readiness';
|
||
const retryRunId = 'retry-ready-run';
|
||
const retryRouter = createDeterministicLaneDefenseRouter({ apiKey });
|
||
const retryRoute = (extraContext = '') =>
|
||
retryRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
retryAgentId,
|
||
retryRunId,
|
||
manifestReadyTools,
|
||
`处理 manifest ready 任务:${retryAgentId}\n任务 ID:${retryAgentId}${extraContext}`,
|
||
),
|
||
});
|
||
for (let attempt = 0; attempt < 8; attempt += 1) {
|
||
if (responseFunctionNames(retryRoute()).includes('respond_to_user')) break;
|
||
}
|
||
for (let retry = 0; retry < 16; retry += 1) {
|
||
assert(
|
||
responseFunctionNames(
|
||
retryRoute(retryObservationContext([staleObservation])),
|
||
).join(',') === 'runtime_tool_command_run_limited',
|
||
`self-test-ready-retry-verification-call-invalid:${retry}`,
|
||
);
|
||
if (retry === 0) {
|
||
let missingObservationCode = null;
|
||
try {
|
||
retryRoute();
|
||
} catch (error) {
|
||
missingObservationCode = error?.code ?? null;
|
||
}
|
||
assert(
|
||
missingObservationCode ===
|
||
`provider-ready-retry-verification-invalid:${retryAgentId}`,
|
||
'self-test-ready-retry-missing-observation-accepted',
|
||
);
|
||
}
|
||
assert(
|
||
responseFunctionNames(
|
||
retryRoute(retryObservationContext([successfulSmokeObservation])),
|
||
).includes('respond_to_user'),
|
||
`self-test-ready-retry-finalization-invalid:${retry}`,
|
||
);
|
||
}
|
||
let retryLimitCode = null;
|
||
try {
|
||
retryRoute(retryObservationContext([staleObservation]));
|
||
} catch (error) {
|
||
retryLimitCode = error?.code ?? null;
|
||
}
|
||
const retryStats = retryRouter.getStats();
|
||
assert(
|
||
retryLimitCode ===
|
||
`provider-ready-run-terminal-duplicate:${retryAgentId}` &&
|
||
retryStats.readyTaskCountsByAgent[retryAgentId]?.run === 1 &&
|
||
retryStats.readyTaskCountsByAgent[retryAgentId]?.completion === 1,
|
||
'self-test-ready-retry-limit-or-exactly-once-invalid',
|
||
);
|
||
|
||
const transientRetryRouter = createDeterministicLaneDefenseRouter({ apiKey });
|
||
const transientRetryRunId = 'transient-retry-ready-run';
|
||
const transientRetryRoute = (extraContext = '') =>
|
||
transientRetryRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
retryAgentId,
|
||
transientRetryRunId,
|
||
manifestReadyTools,
|
||
`处理 manifest ready 任务:${retryAgentId}\n任务 ID:${retryAgentId}${extraContext}`,
|
||
),
|
||
});
|
||
for (let attempt = 0; attempt < 8; attempt += 1) {
|
||
if (
|
||
responseFunctionNames(transientRetryRoute()).includes('respond_to_user')
|
||
) {
|
||
break;
|
||
}
|
||
}
|
||
transientRetryRoute(retryObservationContext([staleObservation]));
|
||
assert(
|
||
responseFunctionNames(
|
||
transientRetryRoute(retryObservationContext([blockedSmokeObservation])),
|
||
).join(',') === 'runtime_tool_command_run_limited' &&
|
||
responseFunctionNames(
|
||
transientRetryRoute(
|
||
retryObservationContext([successfulSmokeObservation]),
|
||
),
|
||
).includes('respond_to_user') &&
|
||
transientRetryRouter.getStats().readyTaskCountsByAgent[retryAgentId]
|
||
?.completion === 1,
|
||
'self-test-ready-transient-verification-retry-invalid',
|
||
);
|
||
|
||
const standaloneRetryAgentId = 'design-foundation';
|
||
const standaloneRetryRunId = 'standalone-retry-ready-run';
|
||
const standaloneRetryRouter = createDeterministicLaneDefenseRouter({
|
||
apiKey,
|
||
});
|
||
const standaloneRetryRoute = (extraContext = '') =>
|
||
standaloneRetryRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
standaloneRetryAgentId,
|
||
standaloneRetryRunId,
|
||
manifestReadyTools,
|
||
`处理 manifest ready 任务:${standaloneRetryAgentId}\n任务 ID:${standaloneRetryAgentId}${extraContext}`,
|
||
),
|
||
});
|
||
for (let attempt = 0; attempt < 8; attempt += 1) {
|
||
if (
|
||
responseFunctionNames(standaloneRetryRoute()).includes('respond_to_user')
|
||
) {
|
||
break;
|
||
}
|
||
}
|
||
const standaloneReplayResponse = standaloneRetryRoute(
|
||
retryObservationContext([successfulSmokeObservation]),
|
||
);
|
||
const standaloneReplayDuplicateCode = captureProviderErrorCode(() =>
|
||
standaloneRetryRoute(retryObservationContext([successfulSmokeObservation])),
|
||
);
|
||
assert(
|
||
responseFunctionNames(standaloneReplayResponse).includes(
|
||
'respond_to_user',
|
||
) &&
|
||
standaloneReplayDuplicateCode ===
|
||
`provider-ready-run-terminal-duplicate:${standaloneRetryAgentId}` &&
|
||
standaloneRetryRouter.getStats().readyTaskCountsByAgent[
|
||
standaloneRetryAgentId
|
||
]?.completion === 1,
|
||
'self-test-ready-standalone-verification-replay-invalid',
|
||
);
|
||
|
||
const concurrentMutationAgentId = 'design-foundation';
|
||
const concurrentMutationRunId = 'concurrent-mutation-ready-run';
|
||
const concurrentMutationRouter = createDeterministicLaneDefenseRouter({
|
||
apiKey,
|
||
});
|
||
const concurrentMutationRoute = (extraContext = '') =>
|
||
concurrentMutationRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
concurrentMutationAgentId,
|
||
concurrentMutationRunId,
|
||
manifestReadyTools,
|
||
`处理 manifest ready 任务:${concurrentMutationAgentId}\n任务 ID:${concurrentMutationAgentId}${extraContext}`,
|
||
),
|
||
});
|
||
for (let attempt = 0; attempt < 8; attempt += 1) {
|
||
if (
|
||
responseFunctionNames(concurrentMutationRoute()).includes(
|
||
'respond_to_user',
|
||
)
|
||
) {
|
||
break;
|
||
}
|
||
}
|
||
assert(
|
||
responseFunctionNames(
|
||
concurrentMutationRoute(
|
||
retryObservationContext([successfulProjectMutationObservation]),
|
||
),
|
||
).join(',') === 'runtime_tool_command_run_limited' &&
|
||
responseFunctionNames(
|
||
concurrentMutationRoute(
|
||
retryObservationContext([successfulSmokeObservation]),
|
||
),
|
||
).includes('respond_to_user') &&
|
||
concurrentMutationRouter.getStats().readyTaskCountsByAgent[
|
||
concurrentMutationAgentId
|
||
]?.completion === 1,
|
||
'self-test-ready-concurrent-mutation-reverification-invalid',
|
||
);
|
||
|
||
const repairedWriterAgentId = 'balance-seed';
|
||
const repairedWriterRunId = 'repaired-writer-ready-run';
|
||
const repairedWriterRouter = createDeterministicLaneDefenseRouter({ apiKey });
|
||
const repairedWriterRoute = (tools = manifestReadyTools, extraContext = '') =>
|
||
repairedWriterRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
repairedWriterAgentId,
|
||
repairedWriterRunId,
|
||
tools,
|
||
`处理 manifest ready 任务:${repairedWriterAgentId}\n任务 ID:${repairedWriterAgentId}${extraContext}`,
|
||
),
|
||
});
|
||
for (let attempt = 0; attempt < 8; attempt += 1) {
|
||
if (
|
||
responseFunctionNames(repairedWriterRoute()).includes('respond_to_user')
|
||
) {
|
||
break;
|
||
}
|
||
}
|
||
const repairedWriterMutation = repairedWriterRoute(
|
||
['runtime_tool_file_write'],
|
||
retryObservationContext([successfulSmokeObservation]),
|
||
);
|
||
const repairedWriterVerification = repairedWriterRoute(
|
||
manifestReadyTools,
|
||
retryObservationContext([successfulProjectMutationObservation]),
|
||
);
|
||
const repairedWriterFinalization = repairedWriterRoute(
|
||
manifestReadyTools,
|
||
retryObservationContext([successfulSmokeObservation]),
|
||
);
|
||
const repairedWriterDuplicateCode = captureProviderErrorCode(() =>
|
||
repairedWriterRoute(
|
||
manifestReadyTools,
|
||
retryObservationContext([successfulSmokeObservation]),
|
||
),
|
||
);
|
||
assert(
|
||
responseFunctionNames(repairedWriterMutation).join(',') ===
|
||
'runtime_tool_file_write' &&
|
||
responseFunctionNames(repairedWriterVerification).join(',') ===
|
||
'runtime_tool_command_run_limited' &&
|
||
responseFunctionNames(repairedWriterFinalization).includes(
|
||
'respond_to_user',
|
||
) &&
|
||
repairedWriterDuplicateCode ===
|
||
`provider-ready-run-terminal-duplicate:${repairedWriterAgentId}` &&
|
||
repairedWriterRouter.getStats().readyTaskCountsByAgent[
|
||
repairedWriterAgentId
|
||
]?.completion === 1,
|
||
'self-test-ready-repaired-writer-reverification-invalid',
|
||
);
|
||
|
||
const projectRevisionDriftAgentId = 'preview-readiness';
|
||
const projectRevisionDriftRunId = 'project-revision-drift-ready-run';
|
||
const projectRevisionDriftRouter = createDeterministicLaneDefenseRouter({
|
||
apiKey,
|
||
});
|
||
const projectRevisionDriftRoute = (extraContext = '') =>
|
||
projectRevisionDriftRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
projectRevisionDriftAgentId,
|
||
projectRevisionDriftRunId,
|
||
manifestReadyTools,
|
||
`处理 manifest ready 任务:${projectRevisionDriftAgentId}\n任务 ID:${projectRevisionDriftAgentId}${extraContext}`,
|
||
),
|
||
});
|
||
for (let attempt = 0; attempt < 8; attempt += 1) {
|
||
if (
|
||
responseFunctionNames(projectRevisionDriftRoute()).includes(
|
||
'respond_to_user',
|
||
)
|
||
) {
|
||
break;
|
||
}
|
||
}
|
||
assert(
|
||
responseFunctionNames(
|
||
projectRevisionDriftRoute(
|
||
retryObservationContext([projectRevisionDriftSmokeObservation]),
|
||
),
|
||
).join(',') === 'runtime_tool_command_run_limited' &&
|
||
responseFunctionNames(
|
||
projectRevisionDriftRoute(
|
||
retryObservationContext([successfulSmokeObservation]),
|
||
),
|
||
).includes('respond_to_user') &&
|
||
projectRevisionDriftRouter.getStats().readyTaskCountsByAgent[
|
||
projectRevisionDriftAgentId
|
||
]?.completion === 1,
|
||
'self-test-ready-project-revision-drift-retry-invalid',
|
||
);
|
||
|
||
const readOnlyRetryAgentId = 'quality-review';
|
||
const readOnlyRetryRunId = 'read-only-retry-ready-run';
|
||
const readOnlyRetryRouter = createDeterministicLaneDefenseRouter({ apiKey });
|
||
const readOnlyRetryRoute = (extraContext = '') =>
|
||
readOnlyRetryRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
readOnlyRetryAgentId,
|
||
readOnlyRetryRunId,
|
||
manifestReadyTools,
|
||
`处理 manifest ready 任务:${readOnlyRetryAgentId}\n任务 ID:${readOnlyRetryAgentId}${extraContext}`,
|
||
),
|
||
});
|
||
for (let attempt = 0; attempt < 8; attempt += 1) {
|
||
if (
|
||
responseFunctionNames(readOnlyRetryRoute()).includes('respond_to_user')
|
||
) {
|
||
break;
|
||
}
|
||
}
|
||
assert(
|
||
responseFunctionNames(
|
||
readOnlyRetryRoute(retryObservationContext([staleObservation])),
|
||
).includes('respond_to_user') &&
|
||
readOnlyRetryRouter.getStats().manifestReadyTaskStaticSmokeCount === 0,
|
||
'self-test-read-only-retry-used-forbidden-verification',
|
||
);
|
||
const responsibilityContract = responsibilityContractEvidence(stats);
|
||
assert(
|
||
responsibilityContract.codeOwnsGameIndex &&
|
||
responsibilityContract.qualityIsReadOnly &&
|
||
responsibilityContract.qualityIndependentOfCodeTiming &&
|
||
responsibilityContract.artOwnsGeneratedAsset &&
|
||
responsibilityContract.contractViolationFree,
|
||
'self-test-responsibility-contract-invalid',
|
||
);
|
||
assert(
|
||
stats.initialDelegationCount === 3 &&
|
||
stats.followupDelegationCount === 1 &&
|
||
stats.supervisorDirectMutationAttemptCount === 1 &&
|
||
stats.unexpectedRequestCount === 0,
|
||
'self-test-provider-core-stats-invalid',
|
||
);
|
||
|
||
const syntheticChildReport = {
|
||
status: 'PASS',
|
||
suite,
|
||
errorCount: 0,
|
||
blocked: [],
|
||
cleanup: { performed: true, kept: false },
|
||
evidence: {
|
||
...Object.fromEntries(
|
||
requiredZeroChildEvidenceFields.map((field) => [field, 0]),
|
||
),
|
||
evidenceCompleteness: 'complete',
|
||
dedicatedZeroInterventionPath: true,
|
||
stdinTaskCount: 1,
|
||
stdinEndedAfterTask: true,
|
||
turnReportOutcome: 'settled',
|
||
parentTaskStatus: 'completed',
|
||
parentRuntimeStatus: 'idle',
|
||
parentRuntimePhase: 'completed',
|
||
laneDefensePlaytestPassed: true,
|
||
laneDefenseAssertionCount: 37,
|
||
laneDefensePassedAssertionCount: 37,
|
||
browserValidationPassed: true,
|
||
staticSmokePassed: true,
|
||
gameIndexChanged: true,
|
||
projectRevisionDelta: 2,
|
||
finalSupervisorAssistantCount: 1,
|
||
professionalAssistantCount: 3,
|
||
providerRequestIdentityCount: stats.requestCount,
|
||
providerLifecycleStartedCount: stats.requestCount,
|
||
providerLifecycleTerminalCount: stats.requestCount,
|
||
providerLifecycleCompletedCount: stats.requestCount,
|
||
},
|
||
};
|
||
const incompletePlaytestReport = structuredClone(syntheticChildReport);
|
||
incompletePlaytestReport.evidence.laneDefensePassedAssertionCount = 36;
|
||
const manualInputReport = structuredClone(syntheticChildReport);
|
||
manualInputReport.evidence.approveInputCount = 1;
|
||
const residualSidecarReport = structuredClone(syntheticChildReport);
|
||
residualSidecarReport.evidence.providerHandoffSidecarCount = 1;
|
||
assert(
|
||
expectedChildReport(syntheticChildReport, { keepProject: false }, stats) &&
|
||
!expectedChildReport(
|
||
incompletePlaytestReport,
|
||
{ keepProject: false },
|
||
stats,
|
||
) &&
|
||
!expectedChildReport(manualInputReport, { keepProject: false }, stats) &&
|
||
!expectedChildReport(
|
||
residualSidecarReport,
|
||
{ keepProject: false },
|
||
stats,
|
||
),
|
||
'self-test-child-hard-gates-invalid',
|
||
);
|
||
|
||
const rootPackage = JSON.parse(
|
||
await fs.readFile(path.join(repoRoot, 'package.json'), 'utf8'),
|
||
);
|
||
const shellPackage = JSON.parse(
|
||
await fs.readFile(path.join(appRoot, 'package.json'), 'utf8'),
|
||
);
|
||
assert(
|
||
shellPackage.scripts?.[
|
||
'agent-runtime:supervisor-autonomous-playable-lane-defense-deterministic-e2e'
|
||
] === 'node scripts/agent-runtime-deterministic-playable-e2e.mjs' &&
|
||
rootPackage.scripts?.[
|
||
'ai-game-creator-shell:agent-runtime:supervisor-autonomous-playable-lane-defense-deterministic-e2e'
|
||
] ===
|
||
'npm --prefix apps/ai-game-creator-shell run agent-runtime:supervisor-autonomous-playable-lane-defense-deterministic-e2e --',
|
||
'self-test-package-command-invalid',
|
||
);
|
||
return {
|
||
status: 'PASS',
|
||
suite: `${wrapperSuite}-self-test`,
|
||
providerUsed: false,
|
||
htmlChars: [...html].length,
|
||
providerStats: stats,
|
||
manifestReadyTasks,
|
||
manifestReadyFailureSamplesValidated: ['missing', 'duplicate'],
|
||
manifestReadyProviderDuplicateRejections: {
|
||
terminal: duplicateTerminalCode,
|
||
run: duplicateRunCode,
|
||
},
|
||
terminalRetryContractsValidated: {
|
||
oldSmokeObservationSingleUse: true,
|
||
readOnlyCommandOnlyRejected: true,
|
||
preCompletionTransientRetryLimit: 16,
|
||
initialCodeRetryLimit: 16,
|
||
initialArtRetryLimit: 16,
|
||
},
|
||
responsibilityContract,
|
||
qualityTimingOrdersValidated: ['before-code', 'after-code'],
|
||
childHardGatesValidated: true,
|
||
packageCommandsRegistered: true,
|
||
};
|
||
}
|
||
|
||
async function runE2e(options) {
|
||
const token = randomUUID();
|
||
const apiKey = `deterministic-runtime-${randomUUID()}`;
|
||
const configDir = await fs.mkdtemp(
|
||
path.join(os.tmpdir(), 'genarrative-deterministic-provider-config-'),
|
||
);
|
||
let provider = null;
|
||
let childResult = null;
|
||
let childReport = null;
|
||
let configRemoved = false;
|
||
let providerStats = null;
|
||
let failureCode = null;
|
||
try {
|
||
if (process.platform !== 'win32') await fs.chmod(configDir, 0o700);
|
||
await fs.writeFile(
|
||
path.join(configDir, configSentinelName),
|
||
`${JSON.stringify({ schemaVersion: configSentinelSchema, token })}\n`,
|
||
{ flag: 'wx', mode: 0o600 },
|
||
);
|
||
provider = await startDeterministicLaneDefenseProvider({ apiKey });
|
||
const config = {
|
||
editorApi: {
|
||
apiKey,
|
||
baseUrl: provider.editorBaseUrl,
|
||
},
|
||
llm: {
|
||
apiKey,
|
||
baseUrl: provider.baseUrl,
|
||
model: deterministicLaneDefenseModel,
|
||
apiKind: 'openai_chat',
|
||
reasoningEffort: 'low',
|
||
stream: false,
|
||
requestTimeoutMs: 30_000,
|
||
maxRetries: 0,
|
||
retryBackoffMs: 100,
|
||
},
|
||
};
|
||
await fs.writeFile(
|
||
path.join(configDir, configFileName),
|
||
`${JSON.stringify(config)}\n`,
|
||
{ flag: 'wx', mode: 0o600 },
|
||
);
|
||
const childArgs = [
|
||
realE2eScript,
|
||
'--suite',
|
||
suite,
|
||
'--config-dir',
|
||
configDir,
|
||
];
|
||
if (options.keepProject) childArgs.push('--keep-project');
|
||
childResult = await runChild(
|
||
childArgs,
|
||
withLoopbackNoProxy({ ...process.env, NO_COLOR: '1' }),
|
||
);
|
||
childReport = parseChildReport(childResult);
|
||
} catch (error) {
|
||
failureCode = error?.code ?? 'deterministic-e2e-unexpected-error';
|
||
} finally {
|
||
if (provider) {
|
||
try {
|
||
await provider.stop();
|
||
providerStats = provider.getStats();
|
||
} catch {
|
||
failureCode ??= 'deterministic-provider-stop-failed';
|
||
}
|
||
}
|
||
try {
|
||
const sentinel = JSON.parse(
|
||
await fs.readFile(path.join(configDir, configSentinelName), 'utf8'),
|
||
);
|
||
assert(
|
||
sentinel.schemaVersion === configSentinelSchema &&
|
||
sentinel.token === token,
|
||
'deterministic-config-sentinel-invalid',
|
||
);
|
||
await fs.rm(configDir, { recursive: true, force: false });
|
||
configRemoved = true;
|
||
} catch (error) {
|
||
failureCode ??= error?.code ?? 'deterministic-config-cleanup-failed';
|
||
}
|
||
}
|
||
|
||
const childPassed =
|
||
childResult?.code === 0 &&
|
||
childResult?.signal === null &&
|
||
!childResult?.error &&
|
||
expectedChildReport(childReport, options, providerStats);
|
||
const providerPassed =
|
||
providerStats?.stopped === true && expectedProviderStats(providerStats);
|
||
const manifestReadyTasks = manifestReadyTaskEvidence(providerStats);
|
||
const responsibilityContract = responsibilityContractEvidence(providerStats);
|
||
const status =
|
||
!failureCode && childPassed && providerPassed && configRemoved
|
||
? 'PASS'
|
||
: 'FAIL';
|
||
if (status !== 'PASS' && !failureCode) {
|
||
failureCode = !childPassed
|
||
? 'deterministic-child-e2e-failed'
|
||
: !providerPassed
|
||
? 'deterministic-provider-contract-failed'
|
||
: 'deterministic-config-not-cleaned';
|
||
}
|
||
return {
|
||
status,
|
||
suite: wrapperSuite,
|
||
providerMode: 'deterministic-loopback-openai-chat',
|
||
delegatedSuite: suite,
|
||
child: childReport,
|
||
provider: providerStats,
|
||
manifestReadyTasks,
|
||
responsibilityContract,
|
||
cleanup: {
|
||
providerStopped: providerStats?.stopped === true,
|
||
configRemoved,
|
||
projectKept: options.keepProject,
|
||
},
|
||
childDiagnostic: childResult ? safeChildDiagnostic(childResult) : null,
|
||
failureCode,
|
||
};
|
||
}
|
||
|
||
const options = parseArguments(process.argv.slice(2));
|
||
const report = options.selfTest ? await runSelfTest() : await runE2e(options);
|
||
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
||
process.exitCode = report.status === 'PASS' ? 0 : 1;
|