e34d797336
合并 master 后 isolated AppData 配置改为私有副本,避免 Windows 安全校验拒绝硬链接。 只读 runner 状态命令显式配置 config dir,避免误读为 runner 未启用。 为 debug 构建增加仅 E2E 显式开启的确定性 Provider 路由开关,并补充回归测试。 兼容 Windows 对私有副本权限位的校验差异。
2031 lines
72 KiB
JavaScript
2031 lines
72 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,
|
||
deterministicLaneDefenseCanonicalHtml,
|
||
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 platformSessionFixtureEnv = 'GENARRATIVE_AGC_PLATFORM_SESSION_FIXTURE';
|
||
const platformSessionFixtureName = '.deterministic-platform-session.json';
|
||
const platformSessionFixtureSchema =
|
||
'genarrative-agc-platform-session-fixture.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 deterministicRuntimeConfig(apiKey, provider) {
|
||
return {
|
||
agentMode: 'provider',
|
||
editorApi: {
|
||
apiKey,
|
||
baseUrl: provider.editorBaseUrl,
|
||
},
|
||
llm: {
|
||
apiKey,
|
||
baseUrl: provider.baseUrl,
|
||
model: deterministicLaneDefenseModel,
|
||
apiKind: 'openai_chat',
|
||
reasoningEffort: 'max',
|
||
stream: false,
|
||
requestTimeoutMs: 30_000,
|
||
maxRetries: 0,
|
||
retryBackoffMs: 100,
|
||
},
|
||
agentLlm: {
|
||
'project-supervisor': {
|
||
reasoningEffort: 'max',
|
||
},
|
||
},
|
||
};
|
||
}
|
||
|
||
function deterministicPlatformSessionFixture(apiKey, provider) {
|
||
return {
|
||
schemaVersion: platformSessionFixtureSchema,
|
||
userId: 'deterministic-e2e-user',
|
||
accessToken: apiKey,
|
||
apiBaseUrl: provider.editorBaseUrl,
|
||
generation: 1,
|
||
};
|
||
}
|
||
|
||
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 expectedProviderStats(stats) {
|
||
const manifestReadyTasks = manifestReadyTaskEvidence(stats);
|
||
const projectSupervisorRuntimePlanningCount =
|
||
Number.isInteger(stats.byAgent?.['project-supervisor']?.planning) &&
|
||
Number.isInteger(stats.interactionExecuteCount)
|
||
? stats.byAgent['project-supervisor'].planning -
|
||
stats.interactionExecuteCount
|
||
: null;
|
||
return (
|
||
Number.isInteger(stats.requestCount) &&
|
||
stats.requestCount >= 40 &&
|
||
stats.planningRequestCount +
|
||
stats.finalReplyRequestCount +
|
||
(stats.contextCompactionRequestCount ?? 0) +
|
||
(stats.imageInspectionRequestCount ?? 0) ===
|
||
stats.requestCount &&
|
||
stats.finalReplyRequestCount === 0 &&
|
||
stats.interactionExecuteCount === 1 &&
|
||
stats.goalContractCount === 1 &&
|
||
stats.actionHistoryCount === 1 &&
|
||
stats.acceptanceUpdateCount === 1 &&
|
||
stats.delegateActionCount === 0 &&
|
||
stats.runStatusCount >= 1 &&
|
||
stats.sourceWriteCount >= 7 &&
|
||
stats.staticSmokeCount >= 4 &&
|
||
stats.previewValidationCount >= 2 &&
|
||
manifestReadyTasks.exactlyOnce &&
|
||
stats.manifestReadyTaskFileWriteCount >= 7 &&
|
||
stats.manifestReadyTaskPreviewValidationCount >= 1 &&
|
||
stats.manifestReadyTaskCanvasGenerationCount >=
|
||
stats.canvasGenerationRequestCount &&
|
||
stats.manifestReadyTaskCanvasGenerationCount <=
|
||
stats.canvasGenerationRequestCount + 16 &&
|
||
stats.imageInspectionRequestCount === 1 &&
|
||
stats.canvasGenerationRequestCount === 3 &&
|
||
stats.canvasDownloadRequestCount === 3 &&
|
||
stats.canvasSliceDownloadRequestCount === 4 &&
|
||
stats.canvasGeneratedAspectRatios?.['16:9'] === 1 &&
|
||
stats.canvasGeneratedAspectRatios?.['1:1'] === 2 &&
|
||
stats.canonicalCodeRunCount === 1 &&
|
||
stats.canonicalCodeAssetListCount === 1 &&
|
||
stats.unexpectedRequestCount === 0 &&
|
||
Object.keys(stats.rejectionCodes ?? {}).length === 0 &&
|
||
projectSupervisorRuntimePlanningCount >= 5 &&
|
||
stats.byAgent?.['project-supervisor']?.finalReply === 0 &&
|
||
stats.byAgent?.['project-supervisor']?.projectMutation === 0 &&
|
||
stats.byAgent?.['code-prototype']?.planning >= 5 &&
|
||
stats.byAgent?.['code-prototype']?.finalReply === 0 &&
|
||
stats.byAgent?.['code-prototype']?.projectMutation === 1 &&
|
||
stats.byAgent?.['quality-review']?.finalReply === 0 &&
|
||
stats.byAgent?.['quality-review']?.projectMutation === 0
|
||
);
|
||
}
|
||
|
||
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 syntheticInteractionExecutePayload() {
|
||
return {
|
||
model: deterministicLaneDefenseModel,
|
||
stream: false,
|
||
reasoning_effort: 'max',
|
||
messages: [
|
||
{
|
||
role: 'system',
|
||
content: '你现在位于统一的 Agent interaction loop。',
|
||
},
|
||
{
|
||
role: 'user',
|
||
content: '用户这轮输入:\n制作一个三消经营游戏',
|
||
},
|
||
],
|
||
tools: ['runtime_execute', 'runtime_resume', 'project_location'].map(
|
||
(name) => ({ type: 'function', function: { name } }),
|
||
),
|
||
};
|
||
}
|
||
|
||
function syntheticUiPrototypeInspectionPayload() {
|
||
return {
|
||
model: deterministicLaneDefenseModel,
|
||
stream: false,
|
||
messages: [
|
||
{
|
||
role: 'system',
|
||
content: '你是游戏界面视觉检查 Agent。只分析画面。',
|
||
},
|
||
{
|
||
role: 'user',
|
||
content: [
|
||
{
|
||
type: 'text',
|
||
text: '请逐项检查 informationHud、gameplaySurface、objectiveEntities、primaryControls、failureRestartFlow、responsiveLayout、implementationClarity、originalTheme,并只返回严格 JSON。',
|
||
},
|
||
{
|
||
type: 'image_url',
|
||
image_url: { url: 'data:image/png;base64,AA==' },
|
||
},
|
||
],
|
||
},
|
||
],
|
||
};
|
||
}
|
||
|
||
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 canonicalHtml = deterministicLaneDefenseCanonicalHtml();
|
||
assert(
|
||
canonicalHtml.includes(visibleCanvasCss) &&
|
||
canonicalHtml.includes("atlasArt.src='../assets/art-spritesheet.png'") &&
|
||
[
|
||
'player.png',
|
||
'blocks-and-targets.png',
|
||
'obstacles-and-scene.png',
|
||
'feedback-effects.png',
|
||
].every((fileName) =>
|
||
canonicalHtml.includes(`../assets/art-spritesheet-slices/${fileName}`),
|
||
) &&
|
||
(canonicalHtml.match(/ctx\.drawImage\(/g) ?? []).length >= 5,
|
||
'self-test-canonical-visible-art-contract-invalid',
|
||
);
|
||
const apiKey = `deterministic-self-test-${randomUUID()}`;
|
||
const runtimeConfig = deterministicRuntimeConfig(apiKey, {
|
||
baseUrl: 'http://127.0.0.1:41001/v1',
|
||
editorBaseUrl: 'http://127.0.0.1:41002',
|
||
});
|
||
assert(
|
||
runtimeConfig.agentMode === 'provider' &&
|
||
runtimeConfig.llm.apiKind === 'openai_chat' &&
|
||
runtimeConfig.llm.reasoningEffort === 'max' &&
|
||
runtimeConfig.agentLlm?.['project-supervisor']?.reasoningEffort ===
|
||
'max' &&
|
||
runtimeConfig.llm.apiKey === apiKey &&
|
||
runtimeConfig.editorApi.apiKey === apiKey,
|
||
'self-test-provider-runtime-config-invalid',
|
||
);
|
||
const captureProviderErrorCode = (operation) => {
|
||
try {
|
||
operation();
|
||
return null;
|
||
} catch (error) {
|
||
return error?.code ?? null;
|
||
}
|
||
};
|
||
const allTools = [
|
||
'update_agent_plan',
|
||
'respond_to_user',
|
||
'runtime_tool_agent_action_history',
|
||
'runtime_tool_agent_acceptance_update',
|
||
'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 rootRunId = 'parent-run';
|
||
const contractFingerprint = 'a'.repeat(64);
|
||
const contractContext = `[Root Goal Contract]\n${JSON.stringify({ contractFingerprint })}`;
|
||
const observationContext = (observations) =>
|
||
`${contractContext}\n已有工具观察:\n${JSON.stringify(observations)}`;
|
||
const goalContractTools = ['runtime_tool_agent_goal_contract'];
|
||
const goalContractRouter = createDeterministicLaneDefenseRouter({ apiKey });
|
||
goalContractRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
'project-supervisor',
|
||
'goal-contract-order-run',
|
||
goalContractTools,
|
||
),
|
||
});
|
||
assert(
|
||
captureProviderErrorCode(() =>
|
||
goalContractRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
'project-supervisor',
|
||
'goal-contract-order-run',
|
||
goalContractTools,
|
||
),
|
||
}),
|
||
) === 'provider-goal-contract-out-of-order',
|
||
'self-test-goal-contract-duplicate-not-rejected',
|
||
);
|
||
|
||
const router = createDeterministicLaneDefenseRouter({ apiKey });
|
||
const route = (agentId, runId, tools = allTools, extraContext = '') =>
|
||
router.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(agentId, runId, tools, extraContext),
|
||
});
|
||
const interactionExecuteResponse = router.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticInteractionExecutePayload(),
|
||
});
|
||
const interactionExecuteCalls = responseFunctionCalls(
|
||
interactionExecuteResponse,
|
||
);
|
||
const interactionExecuteStats = router.getStats();
|
||
assert(
|
||
interactionExecuteCalls.length === 1 &&
|
||
interactionExecuteCalls[0]?.name === 'runtime_execute' &&
|
||
Object.keys(interactionExecuteCalls[0]?.arguments ?? {}).length === 0 &&
|
||
interactionExecuteStats.requestCount === 1 &&
|
||
interactionExecuteStats.planningRequestCount === 1 &&
|
||
interactionExecuteStats.interactionExecuteCount === 1 &&
|
||
interactionExecuteStats.manifestReadyTaskRunCount === 0 &&
|
||
interactionExecuteStats.manifestReadyTaskCompletionCount === 0 &&
|
||
Object.keys(interactionExecuteStats.readyTaskCountsByAgent).length === 0,
|
||
'self-test-interaction-max-execute-sequence-invalid',
|
||
);
|
||
const visualInspectionResponse = router.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticUiPrototypeInspectionPayload(),
|
||
});
|
||
const visualInspection = JSON.parse(
|
||
visualInspectionResponse.choices[0].message.content,
|
||
);
|
||
assert(
|
||
Object.keys(visualInspection?.checks ?? {}).join(',') ===
|
||
'informationHud,gameplaySurface,objectiveEntities,primaryControls,failureRestartFlow,responsiveLayout,implementationClarity,originalTheme' &&
|
||
Object.values(visualInspection.checks).every((value) => value === true) &&
|
||
Array.isArray(visualInspection.issues) &&
|
||
visualInspection.issues.length === 0 &&
|
||
router.getStats().imageInspectionRequestCount === 1 &&
|
||
router.getStats().unexpectedRequestCount === 0,
|
||
'self-test-ui-prototype-inspection-request-not-routed',
|
||
);
|
||
const goalContractResponse = route(
|
||
'project-supervisor',
|
||
rootRunId,
|
||
goalContractTools,
|
||
);
|
||
const goalContractCalls = responseFunctionCalls(goalContractResponse);
|
||
const acceptanceNodes =
|
||
goalContractCalls[0]?.arguments?.input?.acceptanceNodes ?? [];
|
||
assert(
|
||
goalContractCalls.length === 1 &&
|
||
goalContractCalls[0]?.name === 'runtime_tool_agent_goal_contract' &&
|
||
goalContractCalls[0]?.arguments?.input?.outcome?.includes(
|
||
'可完整游玩的原创植物塔防网页游戏',
|
||
) &&
|
||
acceptanceNodes.length === 2 &&
|
||
acceptanceNodes[0]?.criterionId === 'static-current-revision' &&
|
||
JSON.stringify(acceptanceNodes[0]?.requiredEvidence) ===
|
||
JSON.stringify(['tool:command.run_limited']) &&
|
||
acceptanceNodes[1]?.criterionId === 'playable-current-revision' &&
|
||
JSON.stringify(acceptanceNodes[1]?.requiredEvidence) ===
|
||
JSON.stringify(['tool:preview.validate']) &&
|
||
JSON.stringify(acceptanceNodes[1]?.dependsOn) ===
|
||
JSON.stringify(['static-current-revision']),
|
||
'self-test-goal-contract-invalid',
|
||
);
|
||
assert(
|
||
responseFunctionNames(
|
||
route(
|
||
'project-supervisor',
|
||
rootRunId,
|
||
['runtime_tool_task_list', 'runtime_tool_agent_run_status'],
|
||
`${contractContext}\n当前正式 manifest DAG 仍有专业 task 在运行`,
|
||
),
|
||
).join(',') === 'runtime_tool_task_list,runtime_tool_agent_run_status',
|
||
'self-test-manifest-wait-invalid',
|
||
);
|
||
|
||
const visualReadyAgentIds = new Set([
|
||
'art-director',
|
||
'design-foundation',
|
||
'art-asset-plan',
|
||
]);
|
||
const canvasCallsByAgent = new Map();
|
||
const callBatchesByAgent = new Map();
|
||
for (const agentId of deterministicManifestReadyAgentIds) {
|
||
const runId = `manifest-ready-${agentId}`;
|
||
const extraContext =
|
||
`处理 manifest ready 任务:${agentId}\n` + `任务 ID:${agentId}`;
|
||
const readyTools = visualReadyAgentIds.has(agentId)
|
||
? [
|
||
...manifestReadyTools,
|
||
'runtime_tool_canvas_asset_generate',
|
||
'runtime_tool_image_inspect',
|
||
]
|
||
: manifestReadyTools;
|
||
let terminalCompletionCount = 0;
|
||
for (let attempt = 0; attempt < 8; attempt += 1) {
|
||
const attemptTools =
|
||
agentId === 'art-director' && attempt >= 2
|
||
? ['respond_to_user']
|
||
: readyTools;
|
||
const response = route(agentId, runId, attemptTools, extraContext);
|
||
const calls = responseFunctionCalls(response);
|
||
const batches = callBatchesByAgent.get(agentId) ?? [];
|
||
batches.push(calls.map((call) => call.name));
|
||
callBatchesByAgent.set(agentId, batches);
|
||
const canvasCall = calls.find(
|
||
(call) => call.name === 'runtime_tool_canvas_asset_generate',
|
||
);
|
||
if (canvasCall) {
|
||
assert(
|
||
!canvasCallsByAgent.has(agentId),
|
||
`self-test-canvas-generation-duplicate:${agentId}`,
|
||
);
|
||
canvasCallsByAgent.set(agentId, canvasCall);
|
||
}
|
||
if (calls.some((call) => call.name === 'respond_to_user')) {
|
||
terminalCompletionCount += 1;
|
||
break;
|
||
}
|
||
}
|
||
assert(
|
||
terminalCompletionCount === 1,
|
||
`self-test-manifest-ready-terminal-invalid:${agentId}`,
|
||
);
|
||
}
|
||
const artDirectorAssetCall = canvasCallsByAgent.get('art-director');
|
||
const designFoundationAssetCall = canvasCallsByAgent.get('design-foundation');
|
||
const artAssetPlanAssetCall = canvasCallsByAgent.get('art-asset-plan');
|
||
assert(
|
||
JSON.stringify(callBatchesByAgent.get('art-asset-plan')) ===
|
||
JSON.stringify([
|
||
['runtime_tool_asset_list'],
|
||
['runtime_tool_file_write'],
|
||
['runtime_tool_canvas_asset_generate'],
|
||
['runtime_tool_file_read', 'runtime_tool_asset_list'],
|
||
['update_agent_plan', 'respond_to_user'],
|
||
]),
|
||
'self-test-art-asset-plan-revision-stages-invalid',
|
||
);
|
||
assert(
|
||
JSON.stringify(callBatchesByAgent.get('code-prototype')) ===
|
||
JSON.stringify([
|
||
[
|
||
'runtime_tool_file_read',
|
||
'runtime_tool_file_read',
|
||
'runtime_tool_file_read',
|
||
],
|
||
[
|
||
'runtime_tool_file_read',
|
||
'runtime_tool_file_read',
|
||
'runtime_tool_file_read',
|
||
'runtime_tool_asset_list',
|
||
],
|
||
['runtime_tool_file_write'],
|
||
['runtime_tool_command_run_limited'],
|
||
['update_agent_plan', 'respond_to_user'],
|
||
]),
|
||
'self-test-code-prototype-asset-audit-sequence-invalid',
|
||
);
|
||
assert(
|
||
canvasCallsByAgent.size === 3 &&
|
||
artDirectorAssetCall?.arguments?.input?.outputPath ===
|
||
'assets/art-spec.png' &&
|
||
artDirectorAssetCall.arguments.input.aspectRatio === '1:1' &&
|
||
artDirectorAssetCall.arguments.input.assetKind === 'icon-spec' &&
|
||
designFoundationAssetCall?.arguments?.input?.outputPath ===
|
||
'assets/ui-prototype.png' &&
|
||
designFoundationAssetCall.arguments.input.aspectRatio === '16:9' &&
|
||
designFoundationAssetCall.arguments.input.assetKind === 'ui-prototype' &&
|
||
artAssetPlanAssetCall?.arguments?.input?.outputPath ===
|
||
'assets/art-spritesheet.png' &&
|
||
artAssetPlanAssetCall.arguments.input.aspectRatio === '1:1' &&
|
||
artAssetPlanAssetCall.arguments.input.assetKind === 'art-spritesheet',
|
||
'self-test-visual-assets-invalid',
|
||
);
|
||
|
||
const visualRecoveryRouter = createDeterministicLaneDefenseRouter({ apiKey });
|
||
const visualRecoveryRunId = 'visual-recovery-design-foundation';
|
||
const visualRecoveryTools = [
|
||
...manifestReadyTools,
|
||
'runtime_tool_canvas_asset_generate',
|
||
'runtime_tool_image_inspect',
|
||
];
|
||
const visualRecoveryRoute = (
|
||
tools = visualRecoveryTools,
|
||
extraContext = '',
|
||
) =>
|
||
visualRecoveryRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
'design-foundation',
|
||
visualRecoveryRunId,
|
||
tools,
|
||
`处理 manifest ready 任务:design-foundation\n任务 ID:design-foundation${extraContext}`,
|
||
),
|
||
});
|
||
visualRecoveryRoute();
|
||
visualRecoveryRoute();
|
||
const visualRecoveryCanvasResponse = visualRecoveryRoute();
|
||
const visualRecoveryInitialInspectResponse = visualRecoveryRoute();
|
||
assert(
|
||
responseFunctionNames(visualRecoveryCanvasResponse).join(',') ===
|
||
'runtime_tool_canvas_asset_generate' &&
|
||
responseFunctionNames(visualRecoveryInitialInspectResponse).includes(
|
||
'runtime_tool_image_inspect',
|
||
),
|
||
'self-test-ui-prototype-initial-inspection-sequence-invalid',
|
||
);
|
||
const failedInspectObservation = {
|
||
tool: 'image.inspect',
|
||
status: 'failed',
|
||
summary: '视觉模型调用失败',
|
||
detail: 'upstream-422',
|
||
};
|
||
const prematureCompletionResponse = visualRecoveryRoute(
|
||
['update_agent_plan', 'respond_to_user'],
|
||
`\n已有工具观察:\n${JSON.stringify([failedInspectObservation])}`,
|
||
);
|
||
assert(
|
||
responseFunctionNames(prematureCompletionResponse).includes(
|
||
'respond_to_user',
|
||
),
|
||
'self-test-ui-prototype-premature-completion-fixture-invalid',
|
||
);
|
||
const visualBlockedObservation = {
|
||
tool: 'runtime.visual_asset',
|
||
status: 'blocked',
|
||
summary: 'UI 原型视觉检查尚未通过',
|
||
detail:
|
||
'expectedPath=assets/ui-prototype.png · requiredInspection=image.inspect',
|
||
};
|
||
const blockedRetryResponse = visualRecoveryRoute(
|
||
visualRecoveryTools,
|
||
`\n已有工具观察:\n${JSON.stringify([visualBlockedObservation])}`,
|
||
);
|
||
assert(
|
||
responseFunctionNames(blockedRetryResponse).join(',') ===
|
||
'runtime_tool_image_inspect',
|
||
'self-test-ui-prototype-visual-block-did-not-retry-inspection',
|
||
);
|
||
const failedRetryResponse = visualRecoveryRoute(
|
||
visualRecoveryTools,
|
||
`\n已有工具观察:\n${JSON.stringify([failedInspectObservation])}`,
|
||
);
|
||
assert(
|
||
responseFunctionNames(failedRetryResponse).join(',') ===
|
||
'runtime_tool_image_inspect',
|
||
'self-test-ui-prototype-failed-inspection-did-not-retry',
|
||
);
|
||
const successfulInspectObservation = {
|
||
tool: 'image.inspect',
|
||
status: 'ok',
|
||
summary: '视觉检查已完成,共分析 1 张图片',
|
||
detail: null,
|
||
};
|
||
const visualRecoveryFinalResponse = visualRecoveryRoute(
|
||
['update_agent_plan', 'respond_to_user'],
|
||
`\n已有工具观察:\n${JSON.stringify([successfulInspectObservation])}`,
|
||
);
|
||
const visualRecoveryStats = visualRecoveryRouter.getStats();
|
||
assert(
|
||
responseFunctionNames(visualRecoveryFinalResponse).includes(
|
||
'respond_to_user',
|
||
) &&
|
||
visualRecoveryStats.manifestReadyTaskRunCount === 1 &&
|
||
visualRecoveryStats.manifestReadyTaskCompletionCount === 1 &&
|
||
visualRecoveryStats.manifestReadyTaskCanvasGenerationCount === 1 &&
|
||
visualRecoveryStats.byAgent?.['design-foundation']?.projectMutation ===
|
||
3 &&
|
||
visualRecoveryStats.unexpectedRequestCount === 0,
|
||
'self-test-ui-prototype-visual-recovery-not-exactly-once',
|
||
);
|
||
|
||
const finalStaticResponse = route(
|
||
'project-supervisor',
|
||
rootRunId,
|
||
['runtime_tool_command_run_limited'],
|
||
`${contractContext}\n已有工具观察:\nseedTaskCounts: completed=16 running=0 pending=0 waiting=0 failed=0 total=16`,
|
||
);
|
||
assert(
|
||
responseFunctionNames(finalStaticResponse).join(',') ===
|
||
'runtime_tool_command_run_limited',
|
||
'self-test-final-static-not-exclusive',
|
||
);
|
||
const successfulFinalStaticObservation = {
|
||
tool: 'command.run_limited',
|
||
status: 'ok',
|
||
summary: 'game.static_smoke 已完成',
|
||
detail: null,
|
||
};
|
||
const finalPreviewResponse = route(
|
||
'project-supervisor',
|
||
rootRunId,
|
||
['runtime_tool_preview_validate'],
|
||
observationContext([successfulFinalStaticObservation]),
|
||
);
|
||
assert(
|
||
responseFunctionNames(finalPreviewResponse).join(',') ===
|
||
'runtime_tool_preview_validate',
|
||
'self-test-final-preview-not-exclusive',
|
||
);
|
||
const successfulFinalPreviewObservation = {
|
||
tool: 'preview.validate',
|
||
status: 'ok',
|
||
summary: '桌面与移动视口真实试玩已通过',
|
||
detail: null,
|
||
};
|
||
const finalHistoryResponse = route(
|
||
'project-supervisor',
|
||
rootRunId,
|
||
['runtime_tool_agent_action_history'],
|
||
observationContext([successfulFinalPreviewObservation]),
|
||
);
|
||
const finalHistoryCalls = responseFunctionCalls(finalHistoryResponse);
|
||
assert(
|
||
finalHistoryCalls.length === 1 &&
|
||
finalHistoryCalls[0]?.name === 'runtime_tool_agent_action_history' &&
|
||
JSON.stringify(finalHistoryCalls[0]?.arguments?.input) ===
|
||
JSON.stringify({
|
||
runId: null,
|
||
actionId: null,
|
||
tool: null,
|
||
status: 'ok',
|
||
limit: 10,
|
||
}),
|
||
'self-test-final-action-history-invalid',
|
||
);
|
||
const staticActionId = `action-${'1'.repeat(24)}`;
|
||
const previewActionId = `action-${'2'.repeat(24)}`;
|
||
const actionHistoryDetail = JSON.stringify({
|
||
runId: rootRunId,
|
||
count: 2,
|
||
truncated: false,
|
||
outputTruncated: false,
|
||
actions: [
|
||
{
|
||
agentId: 'project-supervisor',
|
||
runId: rootRunId,
|
||
actionId: staticActionId,
|
||
tool: 'command.run_limited',
|
||
status: 'ok',
|
||
},
|
||
{
|
||
agentId: 'project-supervisor',
|
||
runId: rootRunId,
|
||
actionId: previewActionId,
|
||
tool: 'preview.validate',
|
||
status: 'ok',
|
||
},
|
||
],
|
||
});
|
||
const finalAcceptanceResponse = route(
|
||
'project-supervisor',
|
||
rootRunId,
|
||
['runtime_tool_agent_acceptance_update'],
|
||
observationContext([
|
||
{
|
||
tool: 'agent.action_history',
|
||
status: 'ok',
|
||
summary: '已读取当前 Agent 的 2 条终态动作',
|
||
detail: actionHistoryDetail,
|
||
},
|
||
]),
|
||
);
|
||
const finalAcceptanceCalls = responseFunctionCalls(finalAcceptanceResponse);
|
||
const acceptanceInput = finalAcceptanceCalls[0]?.arguments?.input;
|
||
assert(
|
||
finalAcceptanceCalls.length === 1 &&
|
||
finalAcceptanceCalls[0]?.name ===
|
||
'runtime_tool_agent_acceptance_update' &&
|
||
acceptanceInput?.contractFingerprint === contractFingerprint &&
|
||
acceptanceInput?.evaluations?.length === 2 &&
|
||
acceptanceInput.evaluations[0]?.criterionId ===
|
||
'static-current-revision' &&
|
||
acceptanceInput.evaluations[0]?.evidence?.[0]?.actionId ===
|
||
staticActionId &&
|
||
acceptanceInput.evaluations[1]?.criterionId ===
|
||
'playable-current-revision' &&
|
||
acceptanceInput.evaluations[1]?.evidence?.[0]?.actionId ===
|
||
previewActionId,
|
||
'self-test-final-acceptance-update-invalid',
|
||
);
|
||
const finalRespondResponse = route(
|
||
'project-supervisor',
|
||
rootRunId,
|
||
['respond_to_user'],
|
||
observationContext([
|
||
{
|
||
tool: 'agent.acceptance_update',
|
||
status: 'ok',
|
||
summary: 'Acceptance Graph 已更新',
|
||
detail: null,
|
||
},
|
||
]),
|
||
);
|
||
assert(
|
||
responseFunctionNames(finalRespondResponse).join(',') === 'respond_to_user',
|
||
'self-test-final-respond-not-exclusive',
|
||
);
|
||
|
||
const planRejectionRouter = createDeterministicLaneDefenseRouter({ apiKey });
|
||
planRejectionRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
'project-supervisor',
|
||
'plan-rejection-run',
|
||
goalContractTools,
|
||
),
|
||
});
|
||
const planRejectionResponse = planRejectionRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
'project-supervisor',
|
||
'plan-rejection-run',
|
||
['respond_to_user', 'runtime_tool_file_patch', 'runtime_tool_file_write'],
|
||
'当前父 run 已进入只编排模式;本轮只允许收束或安全编排。',
|
||
),
|
||
});
|
||
const planRejectionWaitResponse = planRejectionRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
'project-supervisor',
|
||
'plan-rejection-run',
|
||
['runtime_tool_task_list', 'runtime_tool_agent_run_status'],
|
||
'当前正式 manifest DAG 仍有专业 task 在运行',
|
||
),
|
||
});
|
||
const planRejectionStats = planRejectionRouter.getStats();
|
||
assert(
|
||
responseFunctionNames(planRejectionResponse).join(',') ===
|
||
'respond_to_user' &&
|
||
responseFunctionNames(planRejectionWaitResponse).join(',') ===
|
||
'runtime_tool_task_list,runtime_tool_agent_run_status' &&
|
||
planRejectionStats.byAgent?.['project-supervisor']?.projectMutation ===
|
||
0 &&
|
||
planRejectionStats.delegateActionCount === 0 &&
|
||
planRejectionStats.runStatusCount === 1 &&
|
||
planRejectionStats.unexpectedRequestCount === 0,
|
||
'self-test-supervisor-plan-rejection-did-not-converge-read-only',
|
||
);
|
||
|
||
const earlyFinalReplyRouter = createDeterministicLaneDefenseRouter({
|
||
apiKey,
|
||
});
|
||
assert(
|
||
captureProviderErrorCode(() =>
|
||
earlyFinalReplyRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
'project-supervisor',
|
||
'early-final-reply-run',
|
||
[],
|
||
'给用户一个正常中文回复',
|
||
),
|
||
}),
|
||
) === 'provider-parent-final-reply-before-acceptance',
|
||
'self-test-parent-final-reply-bypassed-acceptance',
|
||
);
|
||
|
||
const acceptanceGuardRunId = 'acceptance-guard-run';
|
||
const acceptanceGuardRouter = createDeterministicLaneDefenseRouter({
|
||
apiKey,
|
||
});
|
||
const acceptanceGuardRoute = (tools, extraContext = '') =>
|
||
acceptanceGuardRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
'project-supervisor',
|
||
acceptanceGuardRunId,
|
||
tools,
|
||
extraContext,
|
||
),
|
||
});
|
||
acceptanceGuardRoute(goalContractTools);
|
||
acceptanceGuardRoute(
|
||
['runtime_tool_command_run_limited'],
|
||
`${contractContext}\n已有工具观察:\nseedTaskCounts: completed=16 running=0 pending=0 waiting=0 failed=0 total=16`,
|
||
);
|
||
acceptanceGuardRoute(
|
||
['runtime_tool_preview_validate'],
|
||
observationContext([successfulFinalStaticObservation]),
|
||
);
|
||
acceptanceGuardRoute(
|
||
['runtime_tool_agent_action_history'],
|
||
observationContext([successfulFinalPreviewObservation]),
|
||
);
|
||
const acceptanceGuardDetail = JSON.parse(actionHistoryDetail);
|
||
acceptanceGuardDetail.runId = acceptanceGuardRunId;
|
||
for (const action of acceptanceGuardDetail.actions) {
|
||
action.runId = acceptanceGuardRunId;
|
||
}
|
||
const actionHistoryObservationFor = (detail) => ({
|
||
tool: 'agent.action_history',
|
||
status: 'ok',
|
||
summary: '已读取当前 Agent 的 2 条终态动作',
|
||
detail: JSON.stringify(detail),
|
||
});
|
||
const ambiguousContractContext = `[Root Goal Contract]\n{"contractFingerprint":"${contractFingerprint}","nested":{"contractFingerprint":"${'b'.repeat(64)}"}}`;
|
||
assert(
|
||
captureProviderErrorCode(() =>
|
||
acceptanceGuardRoute(
|
||
['runtime_tool_agent_acceptance_update'],
|
||
`${ambiguousContractContext}\n已有工具观察:\n${JSON.stringify([
|
||
actionHistoryObservationFor(acceptanceGuardDetail),
|
||
])}`,
|
||
),
|
||
) === 'provider-goal-contract-fingerprint-missing',
|
||
'self-test-ambiguous-goal-contract-fingerprint-accepted',
|
||
);
|
||
const invalidActionHistoryDetail = structuredClone(acceptanceGuardDetail);
|
||
invalidActionHistoryDetail.actions[0].actionId = 'action-invalid';
|
||
assert(
|
||
captureProviderErrorCode(() =>
|
||
acceptanceGuardRoute(
|
||
['runtime_tool_agent_acceptance_update'],
|
||
`${contractContext}\n已有工具观察:\n${JSON.stringify([
|
||
actionHistoryObservationFor(invalidActionHistoryDetail),
|
||
])}`,
|
||
),
|
||
) === 'provider-final-acceptance-evidence-missing',
|
||
'self-test-invalid-action-history-id-accepted',
|
||
);
|
||
assert(
|
||
responseFunctionNames(
|
||
acceptanceGuardRoute(
|
||
['runtime_tool_agent_acceptance_update'],
|
||
`${contractContext}\n已有工具观察:\n${JSON.stringify([
|
||
actionHistoryObservationFor(acceptanceGuardDetail),
|
||
])}`,
|
||
),
|
||
).join(',') === 'runtime_tool_agent_acceptance_update',
|
||
'self-test-acceptance-stage-advanced-after-rejected-history',
|
||
);
|
||
|
||
const stats = router.getStats();
|
||
const manifestReadyTasks = manifestReadyTaskEvidence(stats);
|
||
assert(
|
||
manifestReadyTasks.exactlyOnce &&
|
||
stats.goalContractCount === 1 &&
|
||
stats.actionHistoryCount === 1 &&
|
||
stats.acceptanceUpdateCount === 1 &&
|
||
stats.interactionExecuteCount === 1 &&
|
||
stats.delegateActionCount === 0 &&
|
||
stats.imageInspectionRequestCount === 1 &&
|
||
stats.manifestReadyTaskCanvasGenerationCount === 3 &&
|
||
stats.byAgent?.['project-supervisor']?.projectMutation === 0 &&
|
||
stats.byAgent?.['code-prototype']?.projectMutation === 1 &&
|
||
stats.byAgent?.['quality-review']?.projectMutation === 0,
|
||
'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 projectLockSmokeObservations = [
|
||
{
|
||
tool: 'command.run_limited',
|
||
status: 'failed',
|
||
summary: 'game.static_smoke 无法取得项目验证锁',
|
||
detail: '项目正在被其他写操作占用:$PROJECT_ROOT/.agent/project.lock',
|
||
},
|
||
{
|
||
tool: 'command.run_limited',
|
||
status: 'failed',
|
||
summary: 'game.static_smoke 无法取得项目验证锁',
|
||
detail: '项目正在被其他写操作占用:$PROJECT_ROOT\\.agent\\project.lock',
|
||
},
|
||
];
|
||
const projectRevisionDriftSmokeObservation = {
|
||
tool: 'command.run_limited',
|
||
status: 'blocked',
|
||
summary: '并行项目变更使旧动作过期,旧动作未执行',
|
||
detail:
|
||
'projectRevisionDrift=true · expectedRevision=4 · currentRevision=5 · replanRequired=true',
|
||
};
|
||
const projectRevisionDriftCanvasObservation = {
|
||
tool: 'canvas.asset_generate',
|
||
status: 'blocked',
|
||
summary: '并行项目变更使旧动作过期,旧动作未执行',
|
||
detail:
|
||
'projectRevisionDrift=true · expectedRevision=5 · currentRevision=6 · replanRequired=true',
|
||
};
|
||
const successfulProjectMutationObservation = {
|
||
tool: 'project.patchset',
|
||
status: 'ok',
|
||
summary: 'project.patchset 已原子应用 2 项变更',
|
||
detail: 'checkpointId=checkpoint-concurrent-ready-batch',
|
||
};
|
||
const missingArtAssetObservation = {
|
||
tool: 'runtime.visual_asset',
|
||
status: 'blocked',
|
||
summary: '首版美术素材图尚未按正式视觉流程生成并登记,不能完成任务',
|
||
detail: '[redacted sensitive context]',
|
||
};
|
||
const successfulCanvasMutationObservation = {
|
||
tool: 'canvas.asset_generate',
|
||
status: 'ok',
|
||
summary: '素材画布图片已生成并登记',
|
||
detail: null,
|
||
};
|
||
|
||
const artAssetRecoveryAgentId = 'art-asset-plan';
|
||
const artAssetRecoveryRunId = 'art-asset-visual-recovery-ready-run';
|
||
const artAssetRecoveryTools = [
|
||
...manifestReadyTools,
|
||
'runtime_tool_canvas_asset_generate',
|
||
];
|
||
const artAssetRecoveryRouter = createDeterministicLaneDefenseRouter({
|
||
apiKey,
|
||
});
|
||
const artAssetRecoveryRoute = (extraContext = '') =>
|
||
artAssetRecoveryRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
artAssetRecoveryAgentId,
|
||
artAssetRecoveryRunId,
|
||
artAssetRecoveryTools,
|
||
`处理 manifest ready 任务:${artAssetRecoveryAgentId}\n任务 ID:${artAssetRecoveryAgentId}${extraContext}`,
|
||
),
|
||
});
|
||
for (let attempt = 0; attempt < 8; attempt += 1) {
|
||
if (
|
||
responseFunctionNames(artAssetRecoveryRoute()).includes('respond_to_user')
|
||
) {
|
||
break;
|
||
}
|
||
}
|
||
const artAssetRegenerationResponse = artAssetRecoveryRoute(
|
||
retryObservationContext([missingArtAssetObservation]),
|
||
);
|
||
const artAssetReverificationResponse = artAssetRecoveryRoute(
|
||
retryObservationContext([successfulCanvasMutationObservation]),
|
||
);
|
||
const artAssetRefinalizationResponse = artAssetRecoveryRoute(
|
||
retryObservationContext([successfulSmokeObservation]),
|
||
);
|
||
const artAssetRecoveryStats = artAssetRecoveryRouter.getStats();
|
||
assert(
|
||
responseFunctionNames(artAssetRegenerationResponse).join(',') ===
|
||
'runtime_tool_canvas_asset_generate' &&
|
||
responseFunctionNames(artAssetReverificationResponse).join(',') ===
|
||
'runtime_tool_command_run_limited' &&
|
||
responseFunctionNames(artAssetRefinalizationResponse).includes(
|
||
'respond_to_user',
|
||
) &&
|
||
artAssetRecoveryStats.readyTaskCountsByAgent[artAssetRecoveryAgentId]
|
||
?.completion === 1 &&
|
||
artAssetRecoveryStats.manifestReadyTaskCanvasGenerationCount === 2 &&
|
||
artAssetRecoveryStats.unexpectedRequestCount === 0,
|
||
'self-test-art-asset-visual-recovery-not-exactly-once',
|
||
);
|
||
|
||
const artAssetRevisionDriftRunId = 'art-asset-revision-drift-ready-run';
|
||
const artAssetRevisionDriftRouter = createDeterministicLaneDefenseRouter({
|
||
apiKey,
|
||
});
|
||
const artAssetRevisionDriftRoute = (extraContext = '') =>
|
||
artAssetRevisionDriftRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
artAssetRecoveryAgentId,
|
||
artAssetRevisionDriftRunId,
|
||
artAssetRecoveryTools,
|
||
`处理 manifest ready 任务:${artAssetRecoveryAgentId}\n任务 ID:${artAssetRecoveryAgentId}${extraContext}`,
|
||
),
|
||
});
|
||
const artAssetInitialListResponse = artAssetRevisionDriftRoute();
|
||
const artAssetManifestWriteResponse = artAssetRevisionDriftRoute();
|
||
const artAssetInitialCanvasResponse = artAssetRevisionDriftRoute();
|
||
const artAssetRetryCanvasResponse = artAssetRevisionDriftRoute(
|
||
retryObservationContext([projectRevisionDriftCanvasObservation]),
|
||
);
|
||
const artAssetPostRetryReadResponse = artAssetRevisionDriftRoute(
|
||
retryObservationContext([successfulCanvasMutationObservation]),
|
||
);
|
||
const artAssetRevisionDriftFinalizationResponse =
|
||
artAssetRevisionDriftRoute();
|
||
const artAssetRevisionDriftStats = artAssetRevisionDriftRouter.getStats();
|
||
assert(
|
||
responseFunctionNames(artAssetInitialListResponse).join(',') ===
|
||
'runtime_tool_asset_list' &&
|
||
responseFunctionNames(artAssetManifestWriteResponse).join(',') ===
|
||
'runtime_tool_file_write' &&
|
||
responseFunctionNames(artAssetInitialCanvasResponse).join(',') ===
|
||
'runtime_tool_canvas_asset_generate' &&
|
||
responseFunctionNames(artAssetRetryCanvasResponse).join(',') ===
|
||
'runtime_tool_canvas_asset_generate' &&
|
||
responseFunctionNames(artAssetPostRetryReadResponse).join(',') ===
|
||
'runtime_tool_file_read,runtime_tool_asset_list' &&
|
||
responseFunctionNames(artAssetRevisionDriftFinalizationResponse).includes(
|
||
'respond_to_user',
|
||
) &&
|
||
artAssetRevisionDriftStats.readyTaskCountsByAgent[artAssetRecoveryAgentId]
|
||
?.run === 1 &&
|
||
artAssetRevisionDriftStats.readyTaskCountsByAgent[artAssetRecoveryAgentId]
|
||
?.completion === 1 &&
|
||
artAssetRevisionDriftStats.manifestReadyTaskCanvasGenerationCount === 2 &&
|
||
artAssetRevisionDriftStats.unexpectedRequestCount === 0,
|
||
'self-test-art-asset-revision-drift-recovery-not-exactly-once',
|
||
);
|
||
|
||
const artAssetRevisionDriftLimitRunId =
|
||
'art-asset-revision-drift-limit-ready-run';
|
||
const artAssetRevisionDriftLimitRouter = createDeterministicLaneDefenseRouter(
|
||
{ apiKey },
|
||
);
|
||
const artAssetRevisionDriftLimitRoute = (extraContext = '') =>
|
||
artAssetRevisionDriftLimitRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
artAssetRecoveryAgentId,
|
||
artAssetRevisionDriftLimitRunId,
|
||
artAssetRecoveryTools,
|
||
`处理 manifest ready 任务:${artAssetRecoveryAgentId}\n任务 ID:${artAssetRecoveryAgentId}${extraContext}`,
|
||
),
|
||
});
|
||
artAssetRevisionDriftLimitRoute();
|
||
artAssetRevisionDriftLimitRoute();
|
||
artAssetRevisionDriftLimitRoute();
|
||
for (let retry = 0; retry < 16; retry += 1) {
|
||
assert(
|
||
responseFunctionNames(
|
||
artAssetRevisionDriftLimitRoute(
|
||
retryObservationContext([projectRevisionDriftCanvasObservation]),
|
||
),
|
||
).join(',') === 'runtime_tool_canvas_asset_generate',
|
||
`self-test-art-asset-revision-drift-retry-invalid:${retry}`,
|
||
);
|
||
}
|
||
const artAssetRevisionDriftLimitCode = captureProviderErrorCode(() =>
|
||
artAssetRevisionDriftLimitRoute(
|
||
retryObservationContext([projectRevisionDriftCanvasObservation]),
|
||
),
|
||
);
|
||
const artAssetRevisionDriftLimitStats =
|
||
artAssetRevisionDriftLimitRouter.getStats();
|
||
assert(
|
||
artAssetRevisionDriftLimitCode ===
|
||
'provider-ready-art-canvas-retry-exhausted' &&
|
||
artAssetRevisionDriftLimitStats.readyTaskCountsByAgent[
|
||
artAssetRecoveryAgentId
|
||
]?.run === 1 &&
|
||
artAssetRevisionDriftLimitStats.readyTaskCountsByAgent[
|
||
artAssetRecoveryAgentId
|
||
]?.completion === 0 &&
|
||
artAssetRevisionDriftLimitStats.manifestReadyTaskCanvasGenerationCount ===
|
||
17,
|
||
'self-test-art-asset-revision-drift-limit-invalid',
|
||
);
|
||
|
||
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 projectLockRouter = createDeterministicLaneDefenseRouter({ apiKey });
|
||
const projectLockRunId = 'project-lock-transient-ready-run';
|
||
const projectLockRoute = (tools, extraContext = '') =>
|
||
projectLockRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
preCompletionAgentId,
|
||
projectLockRunId,
|
||
tools,
|
||
`处理 manifest ready 任务:${preCompletionAgentId}\n任务 ID:${preCompletionAgentId}${extraContext}`,
|
||
),
|
||
});
|
||
projectLockRoute(manifestReadyTools);
|
||
for (const observation of projectLockSmokeObservations) {
|
||
assert(
|
||
responseFunctionNames(
|
||
projectLockRoute(
|
||
['runtime_tool_command_run_limited'],
|
||
retryObservationContext([observation]),
|
||
),
|
||
).join(',') === 'runtime_tool_command_run_limited',
|
||
'self-test-ready-project-lock-path-separator-not-retried',
|
||
);
|
||
}
|
||
|
||
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 verifiedDeliveryRepairRouter = createDeterministicLaneDefenseRouter({
|
||
apiKey,
|
||
});
|
||
const verifiedDeliveryRepairRunId = 'verified-delivery-repair-ready-run';
|
||
const verifiedDeliveryRepairRoute = (tools, extraContext = '') =>
|
||
verifiedDeliveryRepairRouter.route({
|
||
authorization: `Bearer ${apiKey}`,
|
||
payload: syntheticPayload(
|
||
retryAgentId,
|
||
verifiedDeliveryRepairRunId,
|
||
tools,
|
||
`处理 manifest ready 任务:${retryAgentId}\n任务 ID:${retryAgentId}${extraContext}`,
|
||
),
|
||
});
|
||
for (let attempt = 0; attempt < 8; attempt += 1) {
|
||
if (
|
||
responseFunctionNames(
|
||
verifiedDeliveryRepairRoute(manifestReadyTools),
|
||
).includes('respond_to_user')
|
||
) {
|
||
break;
|
||
}
|
||
}
|
||
const rejectedVerificationPlan = verifiedDeliveryRepairRoute(
|
||
manifestReadyTools,
|
||
retryObservationContext([staleObservation]),
|
||
);
|
||
const verifiedDeliveryRepairResponse = verifiedDeliveryRepairRoute(
|
||
['respond_to_user'],
|
||
`${retryObservationContext([staleObservation])}\n上一条输出不符合工具计划协议:当前 revision 已通过验证。本次修复的原生工具目录只保留 respond_to_user;必须立即调用它交付专业合同结论。`,
|
||
);
|
||
const verifiedDeliveryRepairStats = verifiedDeliveryRepairRouter.getStats();
|
||
assert(
|
||
responseFunctionNames(rejectedVerificationPlan).join(',') ===
|
||
'runtime_tool_command_run_limited' &&
|
||
responseFunctionNames(verifiedDeliveryRepairResponse).join(',') ===
|
||
'respond_to_user' &&
|
||
verifiedDeliveryRepairStats.readyTaskCountsByAgent[retryAgentId]
|
||
?.completion === 1 &&
|
||
verifiedDeliveryRepairStats.unexpectedRequestCount === 0,
|
||
'self-test-ready-verified-delivery-repair-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',
|
||
);
|
||
assert(
|
||
stats.goalContractCount === 1 &&
|
||
stats.actionHistoryCount === 1 &&
|
||
stats.acceptanceUpdateCount === 1 &&
|
||
stats.delegateActionCount === 0 &&
|
||
stats.manifestReadyTaskCanvasGenerationCount === 3 &&
|
||
stats.byAgent?.['project-supervisor']?.projectMutation === 0 &&
|
||
stats.byAgent?.['code-prototype']?.projectMutation === 1 &&
|
||
stats.byAgent?.['quality-review']?.projectMutation === 0 &&
|
||
stats.unexpectedRequestCount === 0,
|
||
'self-test-provider-core-stats-invalid',
|
||
);
|
||
|
||
const syntheticRuntimeProviderRequestCount =
|
||
stats.requestCount -
|
||
stats.interactionExecuteCount -
|
||
stats.imageInspectionRequestCount;
|
||
|
||
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: syntheticRuntimeProviderRequestCount,
|
||
providerLifecycleStartedCount: syntheticRuntimeProviderRequestCount,
|
||
providerLifecycleTerminalCount: syntheticRuntimeProviderRequestCount,
|
||
providerLifecycleCompletedCount: syntheticRuntimeProviderRequestCount,
|
||
},
|
||
};
|
||
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: {
|
||
readOnlyCommandOnlyRejected: true,
|
||
preCompletionTransientRetryLimit: 16,
|
||
projectLockPathSeparatorsRetried: ['/', '\\'],
|
||
},
|
||
providerRuntimeModeValidated: true,
|
||
interactionExecutionValidated: {
|
||
reasoningEffort: 'max',
|
||
requestCount: stats.interactionExecuteCount,
|
||
responseTools: interactionExecuteCalls.map((call) => call.name),
|
||
readyTaskRunCountBeforeRuntime:
|
||
interactionExecuteStats.manifestReadyTaskRunCount,
|
||
readyTaskCompletionCountBeforeRuntime:
|
||
interactionExecuteStats.manifestReadyTaskCompletionCount,
|
||
},
|
||
goalContractValidated: true,
|
||
finalizationSequenceValidated: [
|
||
'command.run_limited',
|
||
'preview.validate',
|
||
'agent.action_history',
|
||
'agent.acceptance_update',
|
||
'respond_to_user',
|
||
],
|
||
canvasArtifactsValidated: [
|
||
'assets/art-spec.png',
|
||
'assets/ui-prototype.png',
|
||
'assets/art-spritesheet.png',
|
||
],
|
||
uiPrototypeInspectionValidated: true,
|
||
uiPrototypeVisualRecoveryExactlyOnceValidated: true,
|
||
artAssetVisualRecoveryExactlyOnceValidated: true,
|
||
artAssetRevisionDriftRecoveryExactlyOnceValidated: true,
|
||
artAssetRevisionDriftRetryLimitValidated: 16,
|
||
supervisorPlanRejectionMode: 'respond-only-then-read-only-wait',
|
||
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 = deterministicRuntimeConfig(apiKey, provider);
|
||
const fixturePath = path.join(configDir, platformSessionFixtureName);
|
||
await fs.writeFile(
|
||
fixturePath,
|
||
`${JSON.stringify(deterministicPlatformSessionFixture(apiKey, provider))}\n`,
|
||
{ flag: 'wx', mode: 0o600 },
|
||
);
|
||
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',
|
||
[platformSessionFixtureEnv]: fixturePath,
|
||
GENARRATIVE_AGC_DEBUG_PROVIDER_E2E: '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 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,
|
||
finalizationContract: {
|
||
goalContractCount: providerStats?.goalContractCount ?? null,
|
||
actionHistoryCount: providerStats?.actionHistoryCount ?? null,
|
||
acceptanceUpdateCount: providerStats?.acceptanceUpdateCount ?? null,
|
||
delegateActionCount: providerStats?.delegateActionCount ?? null,
|
||
supervisorProjectMutationCount:
|
||
providerStats?.byAgent?.['project-supervisor']?.projectMutation ?? null,
|
||
},
|
||
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;
|