be89296492
拆分 App 认证、壳层、运行配置与项目摘要模块 拆分 Tauri 项目能力与 Rust 测试领域模块 拆分界面测试与 Agent Runtime 真实 E2E 套件 补充源码扫描和客户端模块化文档约定
2329 lines
82 KiB
JavaScript
2329 lines
82 KiB
JavaScript
import {
|
|
assert,
|
|
codedError,
|
|
hashValue,
|
|
isFailedTask,
|
|
sleep,
|
|
} from '../assertions/core.mjs';
|
|
import {
|
|
actionAuditIdentity,
|
|
assertNoPersistedImagePayload,
|
|
auditInputValue,
|
|
auditPatchsetPathsInclude,
|
|
auditPathEquals,
|
|
canonicalAuditInputSummary,
|
|
collectNativeRuntimeToolPlanProtocolEvidence,
|
|
countExactSecrets,
|
|
duplicateCount,
|
|
emptyToolPlanRepairCountsByProtocolErrorKind,
|
|
finalMessageId,
|
|
findSuccessfulToolExecution,
|
|
isNonEmptyString,
|
|
receiptAuditIdentity,
|
|
sumObjectValues,
|
|
validateNativeRuntimeToolPlanProtocolEvidence,
|
|
validateToolActionReplays,
|
|
} from '../assertions/runtime.mjs';
|
|
import { fs, path } from '../dependencies.mjs';
|
|
import {
|
|
claimOwnedRunner,
|
|
ensureOwnedRunnerStableKillSupport,
|
|
prepareIsolatedSuiteAppData,
|
|
} from '../harness/app-data.mjs';
|
|
import {
|
|
isLiveTask,
|
|
listFiles,
|
|
parseAssignedJson,
|
|
readJson,
|
|
readOptionalJsonl,
|
|
relativeProjectPath,
|
|
resolveProjectRelative,
|
|
} from '../harness/io.mjs';
|
|
import { prepareCliBinary, runCli, runProcess } from '../harness/process.mjs';
|
|
import {
|
|
assertResultOrientedDisposableTask,
|
|
seedDisposableProject,
|
|
} from '../harness/project.mjs';
|
|
import {
|
|
collectPartialRuntimeJsonlSurface,
|
|
readLatestJsonFile,
|
|
} from '../harness/reporting.mjs';
|
|
import {
|
|
agentConversationPath,
|
|
assertStructuredPlanAuditSnapshot,
|
|
buildTaskSnapshot,
|
|
confirmPendingActions,
|
|
countLureLeaks,
|
|
countMarkerOutsideRuntimeControl,
|
|
findPendingActions,
|
|
hasExpectedWorkspaceSandboxMetadata,
|
|
inspectStructuredPlanSnapshot,
|
|
killRunnerOnce,
|
|
mainContextBundlePath,
|
|
mainRuntimeStatePath,
|
|
readAllRuntimeEvents,
|
|
readRunnerStatus,
|
|
readTaskSnapshot,
|
|
runnerBootId,
|
|
validateProjectRootPublicLeakBoundary,
|
|
waitForCanonicalRuntime,
|
|
waitForRunnerBootChange,
|
|
} from '../harness/runtime.mjs';
|
|
import {
|
|
commandDiagnosticLineCount,
|
|
commandFailureMarker,
|
|
commandPassedMarker,
|
|
commandRootErrorLine,
|
|
commandRootErrorMarker,
|
|
goalDeliveryPath,
|
|
goalEditedPayload,
|
|
goalFailureEvidenceCanary,
|
|
goalFinalMarker,
|
|
goalInitialMarker,
|
|
goalInitialPayload,
|
|
goalProjectWriteTools,
|
|
goalRuntimeSuite,
|
|
goalSessionId,
|
|
mainAgentId,
|
|
patchedText,
|
|
patchsetCreatedContent,
|
|
patchsetCreatedMarker,
|
|
patchsetCreatedPath,
|
|
pollIntervalMs,
|
|
providerRequestLifecycleSchemaVersion,
|
|
requestedRunId,
|
|
runtimeContextBundleSchemaVersion,
|
|
runTimeoutMs,
|
|
state,
|
|
verificationCommand,
|
|
visibleText,
|
|
} from '../runtime-state.mjs';
|
|
|
|
export async function runGoalRuntimeE2e() {
|
|
await ensureOwnedRunnerStableKillSupport();
|
|
await seedDisposableProject();
|
|
await assertGoalInitialMarkerAbsent('goal-project-seeded');
|
|
state.cliBinary = await prepareCliBinary();
|
|
await prepareIsolatedSuiteAppData();
|
|
assertGoalPayloadUnscripted(goalInitialPayload, 'goal-initial');
|
|
assertGoalPayloadUnscripted(goalEditedPayload, 'goal-edited');
|
|
state.initialTask = {
|
|
chars: [...goalInitialPayload.outcome].length,
|
|
sha256: hashValue(goalInitialPayload.outcome),
|
|
};
|
|
|
|
state.isolatedRunner.launchAttempted = true;
|
|
const started = parseGoalMutation(
|
|
await runCli(
|
|
[
|
|
'--agent-goal-start',
|
|
'--init',
|
|
state.projectRoot,
|
|
mainAgentId,
|
|
goalSessionId,
|
|
requestedRunId,
|
|
'--stdin',
|
|
],
|
|
{
|
|
timeoutMs: 120_000,
|
|
stdin: `${JSON.stringify(goalInitialPayload)}\n`,
|
|
},
|
|
),
|
|
);
|
|
assertGoalMutationIdentity(started, 1, 'goal-start');
|
|
state.goal.goalId = started.goal.goalId;
|
|
state.goal.initialRevision = started.goal.revision;
|
|
state.initialRunId = started.goal.runId;
|
|
state.initialSessionId = started.goal.sessionId;
|
|
await claimOwnedRunner();
|
|
const canonicalRuntime = await waitForCanonicalRuntime();
|
|
assert(
|
|
canonicalRuntime.agentId === mainAgentId &&
|
|
canonicalRuntime.sessionId === state.initialSessionId &&
|
|
canonicalRuntime.runId === state.initialRunId &&
|
|
canonicalRuntime.goalId === state.goal.goalId &&
|
|
canonicalRuntime.goalRevision === state.goal.initialRevision &&
|
|
canonicalRuntime.goalStatus === 'active',
|
|
'goal-start-canonical-runtime-invalid',
|
|
);
|
|
|
|
const initialPending = await waitForGoalRevisionPendingAction({
|
|
revision: state.goal.initialRevision,
|
|
marker: goalInitialMarker,
|
|
codePrefix: 'goal-initial',
|
|
});
|
|
state.goal.initialCompletedStepHashes = [
|
|
...initialPending.plan.completedStepHashes,
|
|
];
|
|
state.goal.initialPending = summarizeGoalPending(initialPending.pending);
|
|
await assertGoalInitialMarkerAbsent(
|
|
'goal-initial-pending-before-edit',
|
|
initialPending.pending.actionId,
|
|
);
|
|
const [deliveryBeforeEdit, finalMarkerBeforeEdit] = await Promise.all([
|
|
fs.lstat(path.join(state.projectRoot, goalDeliveryPath)).catch((error) => {
|
|
if (error?.code === 'ENOENT') return null;
|
|
throw error;
|
|
}),
|
|
countMarkerOutsideRuntimeControl(goalFinalMarker),
|
|
]);
|
|
assert(
|
|
deliveryBeforeEdit === null && finalMarkerBeforeEdit === 0,
|
|
'goal-edited-evidence-present-before-edit',
|
|
);
|
|
state.goal.editedEvidenceAbsentBeforeEdit = true;
|
|
await assertGoalRevisionOneFixtureIsolation();
|
|
await injectGoalRevisionTwoFixtureAndProveFailure();
|
|
state.goal.revisionTwoEditAgentDbBoundary = (
|
|
await readOptionalJsonl(path.join(state.projectRoot, '.agent/agent.db'))
|
|
).length;
|
|
|
|
const edited = parseGoalMutation(
|
|
await runCli(
|
|
[
|
|
'--agent-goal-edit',
|
|
state.projectRoot,
|
|
mainAgentId,
|
|
state.initialSessionId,
|
|
state.goal.goalId,
|
|
String(state.goal.initialRevision),
|
|
'--stdin',
|
|
],
|
|
{
|
|
timeoutMs: 120_000,
|
|
stdin: `${JSON.stringify(goalEditedPayload)}\n`,
|
|
},
|
|
),
|
|
);
|
|
assertGoalMutationIdentity(edited, 2, 'goal-edit');
|
|
state.goal.editedRevision = edited.goal.revision;
|
|
state.goal.editProviderInterrupted = edited.providerInterrupted === true;
|
|
await waitForGoalOldActionBlocked(initialPending.pending);
|
|
await waitForGoalRevisionTwoAgentVerificationFailure();
|
|
|
|
const editedPending = await waitForGoalRevisionPendingAction({
|
|
revision: state.goal.editedRevision,
|
|
codePrefix: 'goal-edited',
|
|
minimumPlanRevision: initialPending.plan.revision + 1,
|
|
requiredCompletedStepHashes: state.goal.initialCompletedStepHashes,
|
|
pendingMatcher: goalPendingMatchesRevisionTwoRepair,
|
|
});
|
|
state.goal.editedPending = summarizeGoalPending(editedPending.pending);
|
|
|
|
const paused = parseGoalMutation(
|
|
await runCli(
|
|
[
|
|
'--agent-goal-pause',
|
|
state.projectRoot,
|
|
mainAgentId,
|
|
state.initialSessionId,
|
|
state.goal.goalId,
|
|
String(state.goal.editedRevision),
|
|
],
|
|
{ timeoutMs: 120_000 },
|
|
),
|
|
);
|
|
assertGoalMutationIdentity(paused, state.goal.editedRevision, 'goal-pause');
|
|
assert(
|
|
paused.goal.status === 'paused' &&
|
|
paused.runtime?.state?.status === 'paused' &&
|
|
paused.runtime?.state?.phase === 'paused',
|
|
'goal-pause-not-durable',
|
|
);
|
|
state.goal.pauseProviderInterrupted = paused.providerInterrupted === true;
|
|
|
|
const beforeKillRunner = await readRunnerStatus();
|
|
state.goal.oldRunnerBootId = runnerBootId(beforeKillRunner);
|
|
assert(
|
|
isNonEmptyString(state.goal.oldRunnerBootId),
|
|
'goal-runner-boot-before-kill-missing',
|
|
);
|
|
state.goal.pauseSnapshot = await captureGoalPausedSnapshot(
|
|
editedPending.pending,
|
|
'goal-paused-before-kill',
|
|
);
|
|
await killRunnerOnce();
|
|
await runCli(['--agent-resume', state.projectRoot], { timeoutMs: 120_000 });
|
|
state.resumed = true;
|
|
const restartedRunner = await waitForRunnerBootChange(
|
|
state.goal.oldRunnerBootId,
|
|
);
|
|
state.goal.newRunnerBootId = runnerBootId(restartedRunner);
|
|
assert(
|
|
isNonEmptyString(state.goal.newRunnerBootId) &&
|
|
state.goal.newRunnerBootId !== state.goal.oldRunnerBootId,
|
|
'goal-runner-boot-did-not-change',
|
|
);
|
|
await claimOwnedRunner(restartedRunner);
|
|
state.goal.executionOwnerRecovered =
|
|
await waitForGoalExecutionOwnerTakeover();
|
|
await assertGoalRemainsPausedAfterRestart(
|
|
state.goal.pauseSnapshot,
|
|
editedPending.pending,
|
|
);
|
|
|
|
const resumed = parseGoalMutation(
|
|
await runCli(
|
|
[
|
|
'--agent-goal-resume',
|
|
state.projectRoot,
|
|
mainAgentId,
|
|
state.initialSessionId,
|
|
state.goal.goalId,
|
|
String(state.goal.editedRevision),
|
|
],
|
|
{ timeoutMs: 120_000 },
|
|
),
|
|
);
|
|
assertGoalMutationIdentity(resumed, state.goal.editedRevision, 'goal-resume');
|
|
assert(
|
|
resumed.goal.status === 'active' &&
|
|
resumed.goal.runId === state.initialRunId &&
|
|
['pending', 'waiting-for-confirmation', 'running'].includes(
|
|
resumed.runtime?.state?.status,
|
|
),
|
|
'goal-explicit-resume-invalid',
|
|
);
|
|
state.identityStable = true;
|
|
|
|
await driveGoalRuntimeToQuiescence();
|
|
state.evidence = await validateGoalRuntimeEvidence();
|
|
assert(state.evidence.secretLeakCount === 0, 'loaded-key-leak-detected');
|
|
}
|
|
|
|
export function goalRevisionOneVerificationFixtureSource() {
|
|
return `import fs from 'node:fs';\nconst html = fs.readFileSync('game/index.html', 'utf8');\nconst agents = fs.readFileSync('AGENTS.md', 'utf8');\nconst passed = html.includes(${JSON.stringify(visibleText)}) && html.includes('<canvas') && html.includes('requestAnimationFrame') && agents.includes('REPOSITORY_CONTEXT_MARKER');\nif (!passed) process.exit(1);\nconsole.log(${JSON.stringify(commandPassedMarker)});\n`;
|
|
}
|
|
|
|
export function goalRevisionTwoVerificationFixtureSource() {
|
|
return `import fs from 'node:fs';\nconst html = fs.readFileSync('game/index.html', 'utf8');\nconst agents = fs.readFileSync('AGENTS.md', 'utf8');\nconst requiredCreatedContent = ${JSON.stringify(patchsetCreatedContent)};\nconst patchsetFile = fs.existsSync('${patchsetCreatedPath}') ? fs.readFileSync('${patchsetCreatedPath}', 'utf8') : '';\nconst passed = html.includes('${patchedText}') && html.includes('<canvas') && html.includes('requestAnimationFrame') && agents.includes('REPOSITORY_CONTEXT_MARKER') && patchsetFile === requiredCreatedContent;\nif (!passed) {\n const rootMarker = [${JSON.stringify(commandRootErrorMarker.slice(0, 20))}, ${JSON.stringify(commandRootErrorMarker.slice(20))}].join('');\n for (let line = 1; line <= ${commandDiagnosticLineCount}; line += 1) {\n if (line === 1) console.error('${commandFailureMarker}');\n if (line === ${commandRootErrorLine}) {\n console.error(\`ROOT_CAUSE marker=\${rootMarker} repairPath=game/index.html old=REAL_E2E_TARGET:before new=${patchedText} createPath=${patchsetCreatedPath} createContentJson=\${JSON.stringify(requiredCreatedContent)}\`);\n } else {\n console.error(\`diagnostic-line-\${String(line).padStart(3, '0')} ${'x'.repeat(72)}\`);\n }\n }\n process.exit(1);\n}\nconsole.log('${commandPassedMarker}');\n`;
|
|
}
|
|
|
|
export async function assertGoalRevisionOneFixtureIsolation() {
|
|
const fixturePath = path.join(state.projectRoot, 'verify-e2e.mjs');
|
|
const fixture = await fs.readFile(fixturePath, 'utf8');
|
|
for (const forbidden of [
|
|
commandFailureMarker,
|
|
commandRootErrorMarker,
|
|
patchedText,
|
|
patchsetCreatedPath,
|
|
patchsetCreatedMarker,
|
|
]) {
|
|
assert(
|
|
!fixture.includes(forbidden),
|
|
'goal-revision-two-clue-visible-in-revision-one',
|
|
);
|
|
}
|
|
const verification = await runProcess(process.execPath, ['verify-e2e.mjs'], {
|
|
cwd: state.projectRoot,
|
|
timeoutMs: 120_000,
|
|
});
|
|
assert(
|
|
verification.stdout.includes(commandPassedMarker),
|
|
'goal-revision-one-fixture-not-passing',
|
|
);
|
|
state.goal.revisionOneFixtureIsolated = true;
|
|
}
|
|
|
|
export async function injectGoalRevisionTwoFixtureAndProveFailure() {
|
|
assert(
|
|
state.goal.revisionOneFixtureIsolated,
|
|
'goal-revision-one-isolation-not-proven',
|
|
);
|
|
const gameHtml = await fs.readFile(
|
|
path.join(state.projectRoot, 'game/index.html'),
|
|
'utf8',
|
|
);
|
|
const patchsetMetadata = await fs
|
|
.lstat(path.join(state.projectRoot, patchsetCreatedPath))
|
|
.catch((error) => {
|
|
if (error?.code === 'ENOENT') return null;
|
|
throw error;
|
|
});
|
|
assert(
|
|
gameHtml.includes('REAL_E2E_TARGET:before') &&
|
|
!gameHtml.includes(patchedText) &&
|
|
patchsetMetadata === null,
|
|
'goal-revision-two-failure-precondition-missing',
|
|
);
|
|
await assertGoalInitialMarkerAbsent('goal-revision-two-fixture-before-write');
|
|
await fs.writeFile(
|
|
path.join(state.projectRoot, 'verify-e2e.mjs'),
|
|
goalRevisionTwoVerificationFixtureSource(),
|
|
);
|
|
await assertGoalInitialMarkerAbsent('goal-revision-two-fixture-after-write');
|
|
state.goal.revisionTwoFixtureInjected = true;
|
|
const verification = await runProcess(process.execPath, ['verify-e2e.mjs'], {
|
|
cwd: state.projectRoot,
|
|
timeoutMs: 120_000,
|
|
allowNonZero: true,
|
|
});
|
|
const exactRootCause = `ROOT_CAUSE marker=${commandRootErrorMarker} repairPath=game/index.html old=REAL_E2E_TARGET:before new=${patchedText} createPath=${patchsetCreatedPath} createContentJson=${JSON.stringify(patchsetCreatedContent)}`;
|
|
assert(
|
|
verification.code === 1 &&
|
|
verification.signal === null &&
|
|
verification.stderr.includes(commandFailureMarker) &&
|
|
verification.stderr.includes(exactRootCause),
|
|
'goal-revision-two-real-failure-not-observed',
|
|
);
|
|
state.goal.revisionTwoHostFailureObserved = true;
|
|
}
|
|
|
|
export function assertGoalPayloadUnscripted(payload, codePrefix) {
|
|
assert(
|
|
payload &&
|
|
isNonEmptyString(payload.outcome) &&
|
|
Array.isArray(payload.constraints) &&
|
|
payload.constraints.length > 0 &&
|
|
Array.isArray(payload.verification) &&
|
|
payload.verification.length > 0,
|
|
`${codePrefix}-payload-invalid`,
|
|
);
|
|
assertResultOrientedDisposableTask(
|
|
[payload.outcome, ...payload.constraints, ...payload.verification].join(
|
|
'\n',
|
|
),
|
|
codePrefix,
|
|
);
|
|
}
|
|
|
|
export function goalPrivateBodyValues() {
|
|
return [
|
|
goalInitialPayload.outcome,
|
|
...goalInitialPayload.constraints,
|
|
...goalInitialPayload.verification,
|
|
goalEditedPayload.outcome,
|
|
...goalEditedPayload.constraints,
|
|
...goalEditedPayload.verification,
|
|
goalInitialMarker,
|
|
goalFinalMarker,
|
|
goalFailureEvidenceCanary,
|
|
];
|
|
}
|
|
|
|
export function goalPublicBodyValues() {
|
|
return [
|
|
goalInitialPayload.outcome,
|
|
...goalInitialPayload.constraints,
|
|
...goalInitialPayload.verification,
|
|
goalEditedPayload.outcome,
|
|
...goalEditedPayload.constraints,
|
|
...goalEditedPayload.verification,
|
|
goalInitialMarker,
|
|
goalFinalMarker,
|
|
goalFailureEvidenceCanary,
|
|
];
|
|
}
|
|
|
|
export function parseGoalMutation(result) {
|
|
const mutation = parseAssignedJson(result.stdout, ['goalMutationJson']);
|
|
assert(
|
|
mutation?.goal &&
|
|
mutation?.runtime?.state &&
|
|
typeof mutation.providerInterrupted === 'boolean',
|
|
'goal-mutation-json-invalid',
|
|
);
|
|
return mutation;
|
|
}
|
|
|
|
export function assertGoalMutationIdentity(
|
|
mutation,
|
|
expectedRevision,
|
|
codePrefix,
|
|
) {
|
|
const goal = mutation.goal;
|
|
const runtime = mutation.runtime.state;
|
|
const payload =
|
|
expectedRevision === state.goal.initialRevision || expectedRevision === 1
|
|
? goalInitialPayload
|
|
: goalEditedPayload;
|
|
const runtimeIdentityMatches =
|
|
runtime.runId === goal.runId &&
|
|
runtime.agentId === goal.agentId &&
|
|
runtime.sessionId === goal.sessionId &&
|
|
runtime.goalId === goal.goalId &&
|
|
runtime.goalRevision === goal.revision &&
|
|
runtime.goalStatus === goal.status;
|
|
const queuedStartMatches =
|
|
codePrefix === 'goal-start' &&
|
|
mutation.runtime.recentTasks?.some(
|
|
(task) =>
|
|
task.agentId === goal.agentId &&
|
|
task.sessionId === goal.sessionId &&
|
|
task.runId === goal.runId &&
|
|
task.goalId === goal.goalId &&
|
|
task.goalRevision === goal.revision &&
|
|
task.goalStatus === goal.status &&
|
|
task.status === 'pending',
|
|
);
|
|
assert(
|
|
goal.schemaVersion === 'game-creator-agent-goal.v1' &&
|
|
isNonEmptyString(goal.projectId) &&
|
|
goal.agentId === mainAgentId &&
|
|
goal.sessionId === goalSessionId &&
|
|
goal.runId === requestedRunId &&
|
|
goal.revision === expectedRevision &&
|
|
goal.outcome === payload.outcome &&
|
|
JSON.stringify(goal.constraints) ===
|
|
JSON.stringify(payload.constraints) &&
|
|
JSON.stringify(goal.verification) ===
|
|
JSON.stringify(payload.verification) &&
|
|
(runtimeIdentityMatches || queuedStartMatches),
|
|
`${codePrefix}-identity-invalid`,
|
|
);
|
|
}
|
|
|
|
export function goalSnapshotFingerprint(goal) {
|
|
// serde_json::Map uses lexicographically sorted keys without preserve_order.
|
|
return hashValue(
|
|
JSON.stringify({
|
|
agentId: goal.agentId,
|
|
constraints: goal.constraints,
|
|
goalId: goal.goalId,
|
|
outcome: goal.outcome,
|
|
projectId: goal.projectId,
|
|
revision: goal.revision,
|
|
runId: goal.runId,
|
|
sessionId: goal.sessionId,
|
|
verification: goal.verification,
|
|
}),
|
|
);
|
|
}
|
|
|
|
export async function readGoalStatus() {
|
|
const result = await runCli(
|
|
[
|
|
'--agent-goal-status',
|
|
state.projectRoot,
|
|
mainAgentId,
|
|
state.initialSessionId,
|
|
],
|
|
{ timeoutMs: 60_000 },
|
|
);
|
|
const goal = parseAssignedJson(result.stdout, ['goalJson']);
|
|
assert(goal && typeof goal === 'object', 'goal-status-json-invalid');
|
|
return goal;
|
|
}
|
|
|
|
export async function assertGoalRuntimeCanProgress(codePrefix) {
|
|
const [taskSnapshot, runtime] = await Promise.all([
|
|
readTaskSnapshot(),
|
|
readJson(mainRuntimeStatePath()).catch(() => null),
|
|
]);
|
|
const current = taskSnapshot.latest.find(
|
|
(task) => task.agentId === mainAgentId && task.runId === state.initialRunId,
|
|
);
|
|
if (current && isFailedTask(current)) {
|
|
throw codedError(`${codePrefix}-runtime-failed`);
|
|
}
|
|
if (
|
|
runtime?.status === 'needs-reconciliation' ||
|
|
runtime?.phase === 'needs-reconciliation'
|
|
) {
|
|
throw codedError(`${codePrefix}-runtime-needs-reconciliation`);
|
|
}
|
|
}
|
|
|
|
export function assertGoalContextSnapshot(
|
|
runtime,
|
|
contextBundle,
|
|
goal,
|
|
codePrefix,
|
|
) {
|
|
const expectedGoalSnapshotFingerprint = goalSnapshotFingerprint(goal);
|
|
assert(
|
|
contextBundle.schemaVersion === runtimeContextBundleSchemaVersion &&
|
|
contextBundle.projectId === goal.projectId &&
|
|
contextBundle.agentId === runtime.agentId &&
|
|
contextBundle.taskId === runtime.taskId &&
|
|
contextBundle.sessionId === runtime.sessionId &&
|
|
contextBundle.runId === runtime.runId &&
|
|
contextBundle.goalId === goal.goalId &&
|
|
contextBundle.goalRevision === goal.revision &&
|
|
contextBundle.goalStatus === 'active' &&
|
|
contextBundle.goalSnapshotFingerprint ===
|
|
expectedGoalSnapshotFingerprint &&
|
|
runtime.goalId === goal.goalId &&
|
|
runtime.goalRevision === goal.revision &&
|
|
runtime.goalStatus === goal.status &&
|
|
runtime.goalOutcome === goal.outcome &&
|
|
JSON.stringify(runtime.goalConstraints) ===
|
|
JSON.stringify(goal.constraints) &&
|
|
JSON.stringify(runtime.goalVerification) ===
|
|
JSON.stringify(goal.verification) &&
|
|
contextBundle.planRevision === runtime.planRevision &&
|
|
contextBundle.planExplanation === runtime.planExplanation &&
|
|
JSON.stringify(contextBundle.planSteps) ===
|
|
JSON.stringify(runtime.planSteps) &&
|
|
contextBundle.activePlanStepIndex === runtime.activePlanStepIndex,
|
|
`${codePrefix}-context-snapshot-mismatch`,
|
|
);
|
|
}
|
|
|
|
export async function readVerifiedGoalPlanSnapshot(codePrefix) {
|
|
const [runtime, contextBundle, records, goal] = await Promise.all([
|
|
readJson(mainRuntimeStatePath()),
|
|
readJson(mainContextBundlePath()),
|
|
readOptionalJsonl(path.join(state.projectRoot, '.agent/agent.db')),
|
|
readGoalStatus(),
|
|
]);
|
|
assert(goal.status === 'active', `${codePrefix}-goal-not-active`);
|
|
const snapshot = inspectStructuredPlanSnapshot(runtime, codePrefix);
|
|
assertGoalContextSnapshot(runtime, contextBundle, goal, codePrefix);
|
|
assertStructuredPlanAuditSnapshot(runtime, records, `${codePrefix}-audit`);
|
|
return { ...snapshot, runtime, contextBundle, goal, records };
|
|
}
|
|
|
|
export function goalPendingDeliveryMutation(pending) {
|
|
const action = pending?.record?.action ?? pending?.action;
|
|
const input = action?.input;
|
|
if (!action || !input || typeof input !== 'object') return null;
|
|
if (action.tool === 'file.write') {
|
|
return {
|
|
tool: action.tool,
|
|
path: input.path,
|
|
content: input.content,
|
|
};
|
|
}
|
|
if (action.tool !== 'project.patchset' || !Array.isArray(input.changes)) {
|
|
return null;
|
|
}
|
|
const matchingChanges = input.changes.filter(
|
|
(change) =>
|
|
change?.operation === 'create' && change?.path === goalDeliveryPath,
|
|
);
|
|
if (matchingChanges.length !== 1) return null;
|
|
return {
|
|
tool: action.tool,
|
|
path: matchingChanges[0].path,
|
|
content: matchingChanges[0].content,
|
|
};
|
|
}
|
|
|
|
export function goalPendingMatchesDelivery(pending, marker) {
|
|
const mutation = goalPendingDeliveryMutation(pending);
|
|
return (
|
|
mutation?.path === goalDeliveryPath && mutation.content === `${marker}\n`
|
|
);
|
|
}
|
|
|
|
export function goalPendingIsStandaloneDeliveryMutation(pending, marker) {
|
|
if (!goalPendingMatchesDelivery(pending, marker)) return false;
|
|
const action = pending?.record?.action ?? pending?.action;
|
|
return (
|
|
action?.tool === 'file.write' ||
|
|
(action?.tool === 'project.patchset' && action.input?.changes?.length === 1)
|
|
);
|
|
}
|
|
|
|
export function goalPendingMatchesRevisionTwoRepair(pending) {
|
|
const action = pending?.record?.action ?? pending?.action;
|
|
const input = action?.input;
|
|
if (!action || !input || typeof input !== 'object') return false;
|
|
if (action.tool === 'file.patch') {
|
|
return input.path === 'game/index.html';
|
|
}
|
|
if (action.tool === 'file.write') {
|
|
return (
|
|
input.path === 'game/index.html' || input.path === patchsetCreatedPath
|
|
);
|
|
}
|
|
return (
|
|
action.tool === 'project.patchset' &&
|
|
Array.isArray(input.changes) &&
|
|
input.changes.some(
|
|
(change) =>
|
|
change?.path === 'game/index.html' ||
|
|
change?.path === patchsetCreatedPath,
|
|
)
|
|
);
|
|
}
|
|
|
|
export function validateGoalPendingAction(pending, plan, revision, codePrefix) {
|
|
const record = pending.record;
|
|
const expectedGoalSnapshotFingerprint = goalSnapshotFingerprint(plan.goal);
|
|
assert(
|
|
record?.schemaVersion === 'game-creator-pending-action.v5' &&
|
|
record.agentId === mainAgentId &&
|
|
record.taskId === plan.runtime.taskId &&
|
|
record.sessionId === state.initialSessionId &&
|
|
record.runId === state.initialRunId &&
|
|
record.goalId === state.goal.goalId &&
|
|
record.goalRevision === revision &&
|
|
record.goalSnapshotFingerprint === expectedGoalSnapshotFingerprint &&
|
|
plan.contextBundle.goalSnapshotFingerprint ===
|
|
expectedGoalSnapshotFingerprint &&
|
|
isNonEmptyString(record.actionId) &&
|
|
isNonEmptyString(record.actionFingerprint) &&
|
|
record.actionId === pending.actionId &&
|
|
record.action?.tool === pending.tool &&
|
|
['pending', 'pending-confirmation'].includes(record.status) &&
|
|
Number.isSafeInteger(record.plannedSteerCursor),
|
|
`${codePrefix}-pending-action-invalid`,
|
|
);
|
|
if (revision === state.goal.initialRevision) {
|
|
assert(
|
|
state.goal.initialGoalSnapshotFingerprint == null ||
|
|
state.goal.initialGoalSnapshotFingerprint ===
|
|
expectedGoalSnapshotFingerprint,
|
|
`${codePrefix}-initial-goal-fingerprint-changed`,
|
|
);
|
|
state.goal.initialGoalSnapshotFingerprint = expectedGoalSnapshotFingerprint;
|
|
} else if (revision === state.goal.editedRevision) {
|
|
assert(
|
|
isNonEmptyString(state.goal.initialGoalSnapshotFingerprint) &&
|
|
expectedGoalSnapshotFingerprint !==
|
|
state.goal.initialGoalSnapshotFingerprint,
|
|
`${codePrefix}-goal-fingerprint-did-not-change`,
|
|
);
|
|
state.goal.editedGoalSnapshotFingerprint = expectedGoalSnapshotFingerprint;
|
|
}
|
|
}
|
|
|
|
export async function waitForGoalRevisionPendingAction({
|
|
revision,
|
|
codePrefix,
|
|
minimumPlanRevision = 1,
|
|
requiredCompletedStepHashes = [],
|
|
marker = null,
|
|
pendingMatcher = null,
|
|
}) {
|
|
const matchesPending =
|
|
pendingMatcher ??
|
|
((pending) => goalPendingMatchesDelivery(pending, marker));
|
|
assert(
|
|
typeof matchesPending === 'function' &&
|
|
(pendingMatcher || isNonEmptyString(marker)),
|
|
`${codePrefix}-pending-matcher-invalid`,
|
|
);
|
|
const deadline = Date.now() + runTimeoutMs;
|
|
let lastError = null;
|
|
while (Date.now() < deadline) {
|
|
await assertGoalInitialMarkerAbsent(`${codePrefix}-poll`);
|
|
const taskSnapshot = await readTaskSnapshot();
|
|
const initial = taskSnapshot.latest.find(
|
|
(task) =>
|
|
task.agentId === mainAgentId && task.runId === state.initialRunId,
|
|
);
|
|
if (initial && isFailedTask(initial)) {
|
|
throw codedError(`${codePrefix}-runtime-failed-before-pending`);
|
|
}
|
|
if (initial && !isLiveTask(initial)) {
|
|
throw codedError(`${codePrefix}-runtime-terminal-before-pending`);
|
|
}
|
|
|
|
let plan = null;
|
|
let targetPending = null;
|
|
try {
|
|
const pendingActions = (await findPendingActions()).filter(
|
|
(pending) =>
|
|
pending.agentId === mainAgentId &&
|
|
pending.runId === state.initialRunId,
|
|
);
|
|
targetPending = pendingActions.find(matchesPending);
|
|
plan = await readVerifiedGoalPlanSnapshot(`${codePrefix}-plan`);
|
|
} catch (error) {
|
|
if (targetPending) {
|
|
throw codedError(`${codePrefix}-pending-contract-invalid`, error);
|
|
}
|
|
lastError = error;
|
|
}
|
|
|
|
if (targetPending) {
|
|
validateGoalPendingAction(targetPending, plan, revision, codePrefix);
|
|
assert(
|
|
plan.revision >= minimumPlanRevision &&
|
|
plan.completedStepHashes.length > 0 &&
|
|
plan.incompleteStepCount > 0 &&
|
|
requiredCompletedStepHashes.every((stepHash) =>
|
|
plan.completedStepHashes.includes(stepHash),
|
|
),
|
|
`${codePrefix}-partial-plan-missing`,
|
|
);
|
|
const messages = await readOptionalJsonl(
|
|
agentConversationPath(mainAgentId, state.initialSessionId),
|
|
);
|
|
assert(
|
|
messages.filter((message) => message.role === 'assistant').length === 0,
|
|
`${codePrefix}-assistant-before-control-point`,
|
|
);
|
|
return { pending: targetPending, plan };
|
|
}
|
|
|
|
await confirmPendingActions(null, (pending) => !matchesPending(pending));
|
|
await sleep(pollIntervalMs);
|
|
}
|
|
throw codedError(`${codePrefix}-pending-action-timeout`, lastError);
|
|
}
|
|
|
|
export function summarizeGoalPending(pending) {
|
|
return {
|
|
actionId: pending.actionId,
|
|
actionFingerprint: pending.record.actionFingerprint,
|
|
goalRevision: pending.record.goalRevision,
|
|
schemaVersion: pending.record.schemaVersion,
|
|
tool: pending.tool,
|
|
};
|
|
}
|
|
|
|
export function goalOldActionExecutionEvidence(records, actionId) {
|
|
const executing = records.filter(
|
|
(record) =>
|
|
record.actionId === actionId &&
|
|
[
|
|
'agent.runtime.tool_action.executing',
|
|
'agent.runtime.tool_confirmation.approved',
|
|
].includes(record.recordType),
|
|
);
|
|
const successfulReceipts = records.filter(
|
|
(record) =>
|
|
record.recordType === 'agent.runtime.action_receipt' &&
|
|
record.actionId === actionId &&
|
|
['ok', 'command-failed'].includes(record.status),
|
|
);
|
|
return {
|
|
executionCount: executing.length,
|
|
successfulReceiptCount: successfulReceipts.length,
|
|
};
|
|
}
|
|
|
|
export async function waitForGoalOldActionBlocked(initialPending) {
|
|
const deadline = Date.now() + 120_000;
|
|
let lastError = null;
|
|
while (Date.now() < deadline) {
|
|
await assertGoalRuntimeCanProgress('goal-old-action-block');
|
|
await assertGoalInitialMarkerAbsent(
|
|
'goal-old-action-block-poll',
|
|
initialPending.actionId,
|
|
);
|
|
try {
|
|
const [runtime, contextBundle, records, goal, oldMarkerCount] =
|
|
await Promise.all([
|
|
readJson(mainRuntimeStatePath()),
|
|
readJson(mainContextBundlePath()),
|
|
readOptionalJsonl(path.join(state.projectRoot, '.agent/agent.db')),
|
|
readGoalStatus(),
|
|
countMarkerOutsideRuntimeControl(goalInitialMarker),
|
|
]);
|
|
const execution = goalOldActionExecutionEvidence(
|
|
records,
|
|
initialPending.actionId,
|
|
);
|
|
assert(
|
|
execution.executionCount === 0 &&
|
|
execution.successfulReceiptCount === 0 &&
|
|
oldMarkerCount === 0,
|
|
'goal-old-action-executed',
|
|
);
|
|
const blocked = (contextBundle.observations ?? []).filter(
|
|
(observation) =>
|
|
observation?.tool === 'runtime.goal' &&
|
|
observation?.status === 'blocked',
|
|
);
|
|
const blockedReceipts = records.filter(
|
|
(record) =>
|
|
record.recordType === 'agent.runtime.action_receipt' &&
|
|
record.agentId === mainAgentId &&
|
|
record.runId === state.initialRunId &&
|
|
record.actionId === initialPending.actionId &&
|
|
record.tool === 'runtime.goal' &&
|
|
record.status === 'blocked',
|
|
);
|
|
if (
|
|
goal.revision === state.goal.editedRevision &&
|
|
goal.status === 'active' &&
|
|
runtime.runId === state.initialRunId &&
|
|
runtime.sessionId === state.initialSessionId &&
|
|
runtime.goalId === state.goal.goalId &&
|
|
runtime.goalRevision === state.goal.editedRevision &&
|
|
contextBundle.schemaVersion === runtimeContextBundleSchemaVersion &&
|
|
contextBundle.goalId === state.goal.goalId &&
|
|
contextBundle.goalRevision === state.goal.editedRevision &&
|
|
contextBundle.goalSnapshotFingerprint ===
|
|
goalSnapshotFingerprint(goal) &&
|
|
contextBundle.goalSnapshotFingerprint !==
|
|
state.goal.initialGoalSnapshotFingerprint &&
|
|
blocked.length >= 1 &&
|
|
blockedReceipts.length === 1
|
|
) {
|
|
state.goal.initialPending.blockedObservationCount = blocked.length;
|
|
state.goal.initialPending.blockedReceiptCount = blockedReceipts.length;
|
|
return;
|
|
}
|
|
lastError = codedError('goal-old-action-block-not-yet-observed');
|
|
} catch (error) {
|
|
lastError = error;
|
|
}
|
|
await sleep(pollIntervalMs);
|
|
}
|
|
throw codedError('goal-old-action-block-timeout', lastError);
|
|
}
|
|
|
|
export async function waitForGoalRevisionTwoAgentVerificationFailure() {
|
|
assert(
|
|
state.goal.revisionTwoFixtureInjected === true &&
|
|
state.goal.revisionTwoHostFailureObserved === true &&
|
|
Number.isSafeInteger(state.goal.revisionTwoEditAgentDbBoundary),
|
|
'goal-revision-two-agent-failure-precondition-invalid',
|
|
);
|
|
const deadline = Date.now() + runTimeoutMs;
|
|
let lastError = null;
|
|
while (Date.now() < deadline) {
|
|
await assertGoalRuntimeCanProgress('goal-revision-two-agent-failure');
|
|
await assertGoalInitialMarkerAbsent('goal-revision-two-verify-poll');
|
|
const records = await readOptionalJsonl(
|
|
path.join(state.projectRoot, '.agent/agent.db'),
|
|
);
|
|
const failure = findGoalRevisionTwoAgentVerificationFailure(records);
|
|
if (failure) {
|
|
const contextBundle = await readJson(mainContextBundlePath());
|
|
const failedObservations = (contextBundle.observations ?? []).filter(
|
|
(observation) =>
|
|
observation?.tool === 'project.verify' &&
|
|
observation?.status === 'failed',
|
|
);
|
|
if (failedObservations.length === 0) {
|
|
lastError = codedError(
|
|
'goal-revision-two-failed-observation-not-checkpointed',
|
|
);
|
|
await sleep(pollIntervalMs);
|
|
continue;
|
|
}
|
|
const logPath = resolveProjectRelative(failure.audit.logPath);
|
|
assert(
|
|
relativeProjectPath(logPath).startsWith('.agent/') &&
|
|
failure.audit.exitCode === 1 &&
|
|
failure.audit.timedOut === false,
|
|
'goal-revision-two-failed-verification-audit-invalid',
|
|
);
|
|
const log = await fs.readFile(logPath);
|
|
assert(
|
|
countExactSecrets(log, [commandRootErrorMarker]) === 1 &&
|
|
log.includes(Buffer.from(commandFailureMarker)),
|
|
'goal-revision-two-failed-verification-log-invalid',
|
|
);
|
|
state.goal.revisionTwoFailureObserved = true;
|
|
state.goal.revisionTwoFailureExitCode = failure.audit.exitCode;
|
|
state.goal.revisionTwoFailureFingerprint = hashValue(
|
|
Buffer.concat([
|
|
Buffer.from(
|
|
`${failure.audit.actionId}\0${failure.audit.actionFingerprint}\0`,
|
|
),
|
|
log,
|
|
]),
|
|
);
|
|
state.goal.revisionTwoAgentDbBoundary = failure.observationIndex + 1;
|
|
return;
|
|
}
|
|
|
|
const pendingActions = (await findPendingActions()).filter(
|
|
(pending) =>
|
|
pending.agentId === mainAgentId && pending.runId === state.initialRunId,
|
|
);
|
|
const unsupportedEarlyWrite = pendingActions.find(
|
|
(pending) =>
|
|
goalProjectWriteTools.has(pending.tool) &&
|
|
!goalPendingIsStandaloneDeliveryMutation(pending, goalFinalMarker),
|
|
);
|
|
assert(
|
|
!unsupportedEarlyWrite,
|
|
'goal-revision-two-repair-before-failed-verification',
|
|
);
|
|
await confirmPendingActions(
|
|
new Set([
|
|
'project.checkpoint',
|
|
'project.verify',
|
|
'file.write',
|
|
'project.patchset',
|
|
]),
|
|
(pending) =>
|
|
pending.tool === 'project.checkpoint' ||
|
|
pending.tool === 'project.verify' ||
|
|
goalPendingIsStandaloneDeliveryMutation(pending, goalFinalMarker),
|
|
);
|
|
lastError = codedError('goal-revision-two-agent-failure-not-yet-observed');
|
|
await sleep(pollIntervalMs);
|
|
}
|
|
throw codedError('goal-revision-two-agent-failure-timeout', lastError);
|
|
}
|
|
|
|
export function findGoalRevisionTwoAgentVerificationFailure(records) {
|
|
for (
|
|
let auditIndex = state.goal.revisionTwoEditAgentDbBoundary;
|
|
auditIndex < records.length;
|
|
auditIndex += 1
|
|
) {
|
|
const audit = records[auditIndex];
|
|
if (
|
|
audit.recordType !== 'agent.runtime.project.verify' ||
|
|
audit.agentId !== mainAgentId ||
|
|
audit.runId !== state.initialRunId ||
|
|
audit.status !== 'failed' ||
|
|
audit.exitCode !== 1 ||
|
|
audit.timedOut !== false ||
|
|
!['test', 'check:e2e'].includes(audit.script) ||
|
|
audit.expectedCommand !== verificationCommand ||
|
|
!isNonEmptyString(audit.actionId) ||
|
|
!isNonEmptyString(audit.actionFingerprint) ||
|
|
!isNonEmptyString(audit.logPath) ||
|
|
!hasExpectedWorkspaceSandboxMetadata(audit)
|
|
) {
|
|
continue;
|
|
}
|
|
let startIndex = -1;
|
|
for (let index = auditIndex - 1; index >= 0; index -= 1) {
|
|
const record = records[index];
|
|
if (
|
|
record.agentId === mainAgentId &&
|
|
record.runId === state.initialRunId &&
|
|
record.tool === 'project.verify' &&
|
|
record.actionId === audit.actionId &&
|
|
record.actionFingerprint === audit.actionFingerprint &&
|
|
[
|
|
'agent.runtime.tool_action.executing',
|
|
'agent.runtime.tool_confirmation.approved',
|
|
].includes(record.recordType)
|
|
) {
|
|
startIndex = index;
|
|
break;
|
|
}
|
|
}
|
|
const observationIndex = records.findIndex(
|
|
(record, index) =>
|
|
index > auditIndex &&
|
|
record.recordType === 'agent.runtime.tool_observation' &&
|
|
record.agentId === mainAgentId &&
|
|
record.runId === state.initialRunId &&
|
|
record.tool === 'project.verify' &&
|
|
record.actionId === audit.actionId &&
|
|
record.status === 'failed',
|
|
);
|
|
if (
|
|
startIndex < state.goal.revisionTwoEditAgentDbBoundary ||
|
|
observationIndex < 0
|
|
) {
|
|
continue;
|
|
}
|
|
const unsupportedEarlyWrite = records
|
|
.slice(state.goal.revisionTwoEditAgentDbBoundary, observationIndex + 1)
|
|
.some(
|
|
(record) =>
|
|
goalProjectWriteTools.has(record.tool) &&
|
|
!state.goal.preFailureDeliveryActionIds.has(record.actionId) &&
|
|
[
|
|
'agent.runtime.tool_action.executing',
|
|
'agent.runtime.tool_confirmation.approved',
|
|
].includes(record.recordType),
|
|
);
|
|
assert(
|
|
!unsupportedEarlyWrite,
|
|
'goal-revision-two-repair-executed-before-failure',
|
|
);
|
|
return { audit, auditIndex, observationIndex, startIndex };
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export async function readGoalRuntimePersistence() {
|
|
const taskSnapshot = await readTaskSnapshot();
|
|
const events = await readAllRuntimeEvents();
|
|
const [
|
|
agentDb,
|
|
conversations,
|
|
activity,
|
|
output,
|
|
runtimeState,
|
|
contextBundle,
|
|
goal,
|
|
] = await Promise.all([
|
|
readOptionalJsonl(path.join(state.projectRoot, '.agent/agent.db')),
|
|
readOptionalJsonl(
|
|
agentConversationPath(mainAgentId, state.initialSessionId),
|
|
),
|
|
readOptionalJsonl(path.join(state.projectRoot, '.agent/activity.jsonl')),
|
|
readOptionalJsonl(path.join(state.projectRoot, '.agent/output.jsonl')),
|
|
readJson(mainRuntimeStatePath()),
|
|
readJson(mainContextBundlePath()),
|
|
readGoalStatus(),
|
|
]);
|
|
const pendingActions = (await findPendingActions()).filter(
|
|
(pending) =>
|
|
pending.agentId === mainAgentId && pending.runId === state.initialRunId,
|
|
);
|
|
return {
|
|
taskSnapshot,
|
|
events,
|
|
agentDb,
|
|
conversations,
|
|
activity,
|
|
output,
|
|
runtimeState,
|
|
contextBundle,
|
|
goal,
|
|
pendingActions,
|
|
};
|
|
}
|
|
|
|
export async function captureGoalPausedSnapshot(expectedPending, codePrefix) {
|
|
await assertGoalInitialMarkerAbsent(`${codePrefix}-marker-check`);
|
|
const persistence = await readGoalRuntimePersistence();
|
|
const {
|
|
taskSnapshot,
|
|
events,
|
|
agentDb,
|
|
conversations,
|
|
runtimeState,
|
|
contextBundle,
|
|
goal,
|
|
pendingActions,
|
|
} = persistence;
|
|
const latest = taskSnapshot.latest.find(
|
|
(task) => task.agentId === mainAgentId && task.runId === state.initialRunId,
|
|
);
|
|
const targetRuns = [
|
|
...new Set(
|
|
taskSnapshot.all
|
|
.filter((task) => task.agentId === mainAgentId)
|
|
.map((task) => task.runId),
|
|
),
|
|
].sort();
|
|
const pending = pendingActions.find(
|
|
(candidate) => candidate.actionId === expectedPending.actionId,
|
|
);
|
|
assert(
|
|
goal.goalId === state.goal.goalId &&
|
|
goal.revision === state.goal.editedRevision &&
|
|
goal.status === 'paused' &&
|
|
runtimeState.agentId === mainAgentId &&
|
|
runtimeState.sessionId === state.initialSessionId &&
|
|
runtimeState.runId === state.initialRunId &&
|
|
runtimeState.goalId === state.goal.goalId &&
|
|
runtimeState.goalRevision === state.goal.editedRevision &&
|
|
runtimeState.goalStatus === 'paused' &&
|
|
runtimeState.status === 'paused' &&
|
|
runtimeState.phase === 'paused' &&
|
|
latest?.status === 'paused' &&
|
|
latest?.phase === 'paused' &&
|
|
JSON.stringify(targetRuns) === JSON.stringify([state.initialRunId]) &&
|
|
contextBundle.schemaVersion === runtimeContextBundleSchemaVersion &&
|
|
contextBundle.goalId === state.goal.goalId &&
|
|
contextBundle.goalRevision === state.goal.editedRevision &&
|
|
contextBundle.goalStatus === 'active' &&
|
|
pending?.record?.schemaVersion === 'game-creator-pending-action.v5' &&
|
|
pending.record.goalId === state.goal.goalId &&
|
|
pending.record.goalRevision === state.goal.editedRevision &&
|
|
pending.record.goalSnapshotFingerprint ===
|
|
contextBundle.goalSnapshotFingerprint &&
|
|
contextBundle.goalSnapshotFingerprint === goalSnapshotFingerprint(goal) &&
|
|
contextBundle.goalSnapshotFingerprint ===
|
|
state.goal.editedGoalSnapshotFingerprint &&
|
|
contextBundle.goalSnapshotFingerprint !==
|
|
state.goal.initialGoalSnapshotFingerprint &&
|
|
conversations.filter((message) => message.role === 'assistant').length ===
|
|
0,
|
|
`${codePrefix}-state-invalid`,
|
|
);
|
|
const planProtocolCount = agentDb.filter(
|
|
(record) =>
|
|
record.recordType === 'agent.runtime.tool_plan.protocol' &&
|
|
record.agentId === mainAgentId &&
|
|
record.runId === state.initialRunId,
|
|
).length;
|
|
const actionProgressCount = agentDb.filter(
|
|
(record) =>
|
|
record.agentId === mainAgentId &&
|
|
record.runId === state.initialRunId &&
|
|
[
|
|
'agent.runtime.tool_action.executing',
|
|
'agent.runtime.tool_action.observed',
|
|
'agent.runtime.tool_confirmation.approved',
|
|
'agent.runtime.action_receipt',
|
|
].includes(record.recordType),
|
|
).length;
|
|
const providerRequestStartedCount = agentDb.filter(
|
|
(record) =>
|
|
record.recordType === 'agent.runtime.provider_request.lifecycle' &&
|
|
record.agentId === mainAgentId &&
|
|
record.runId === state.initialRunId &&
|
|
record.status === 'started',
|
|
).length;
|
|
const signaturePayload = {
|
|
goalSha256: hashValue(JSON.stringify(goal)),
|
|
runtimeSha256: hashValue(JSON.stringify(runtimeState)),
|
|
contextSha256: hashValue(JSON.stringify(contextBundle)),
|
|
pendingSha256: hashValue(JSON.stringify(pending.record)),
|
|
taskCount: taskSnapshot.all.filter(
|
|
(task) =>
|
|
task.agentId === mainAgentId && task.runId === state.initialRunId,
|
|
).length,
|
|
eventCount: events.filter(
|
|
(event) =>
|
|
event.agentId === mainAgentId && event.runId === state.initialRunId,
|
|
).length,
|
|
agentDbCount: agentDb.filter(
|
|
(record) =>
|
|
record.agentId === mainAgentId && record.runId === state.initialRunId,
|
|
).length,
|
|
conversationCount: conversations.length,
|
|
assistantCount: conversations.filter(
|
|
(message) => message.role === 'assistant',
|
|
).length,
|
|
planProtocolCount,
|
|
providerRequestStartedCount,
|
|
actionProgressCount,
|
|
planRevision: runtimeState.planRevision,
|
|
pendingActionIdHash: hashValue(pending.actionId),
|
|
};
|
|
return {
|
|
...signaturePayload,
|
|
signature: hashValue(JSON.stringify(signaturePayload)),
|
|
};
|
|
}
|
|
|
|
export async function assertGoalRemainsPausedAfterRestart(
|
|
baseline,
|
|
expectedPending,
|
|
) {
|
|
let latest = null;
|
|
for (let poll = 0; poll < 4; poll += 1) {
|
|
latest = await captureGoalPausedSnapshot(
|
|
expectedPending,
|
|
'goal-paused-after-restart',
|
|
);
|
|
assert(
|
|
latest.signature === baseline.signature,
|
|
'goal-paused-evidence-progressed-after-restart',
|
|
);
|
|
await sleep(1_000);
|
|
}
|
|
state.goal.pausedAfterRestart = latest;
|
|
}
|
|
|
|
export async function waitForGoalExecutionOwnerTakeover() {
|
|
const ownerPath = path.join(
|
|
state.projectRoot,
|
|
'.agent/runtime/execution-owner.json',
|
|
);
|
|
const deadline = Date.now() + 30_000;
|
|
while (Date.now() < deadline) {
|
|
const owner = await readJson(ownerPath).catch(() => null);
|
|
if (
|
|
Number.isSafeInteger(owner?.protocolVersion) &&
|
|
owner.protocolVersion > 0 &&
|
|
Number.isSafeInteger(owner.pid) &&
|
|
owner.pid > 1 &&
|
|
owner.bootId === state.goal.newRunnerBootId &&
|
|
owner.recoveredFromBootId === state.goal.oldRunnerBootId
|
|
) {
|
|
return true;
|
|
}
|
|
await sleep(100);
|
|
}
|
|
throw codedError('goal-execution-owner-takeover-timeout');
|
|
}
|
|
|
|
export async function driveGoalRuntimeToQuiescence() {
|
|
const deadline = Date.now() + runTimeoutMs;
|
|
let quietPolls = 0;
|
|
while (Date.now() < deadline) {
|
|
await assertGoalInitialMarkerAbsent('goal-quiescence-poll');
|
|
await confirmPendingActions();
|
|
const [snapshot, goal] = await Promise.all([
|
|
readTaskSnapshot(),
|
|
readGoalStatus(),
|
|
]);
|
|
const initial = snapshot.latest.find(
|
|
(task) =>
|
|
task.agentId === mainAgentId && task.runId === state.initialRunId,
|
|
);
|
|
if (initial && isFailedTask(initial)) {
|
|
throw codedError('goal-runtime-failed');
|
|
}
|
|
if (goal.status === 'needs-reconciliation') {
|
|
throw codedError('goal-runtime-needs-reconciliation');
|
|
}
|
|
const pending = await findPendingActions();
|
|
const completed =
|
|
initial?.status === 'completed' &&
|
|
initial?.phase === 'completed' &&
|
|
goal.status === 'completed';
|
|
const hasLive = snapshot.latest.some(isLiveTask);
|
|
if (completed && !hasLive && pending.length === 0) {
|
|
quietPolls += 1;
|
|
if (quietPolls >= 3) return;
|
|
} else {
|
|
quietPolls = 0;
|
|
}
|
|
await sleep(pollIntervalMs);
|
|
}
|
|
throw codedError('goal-runtime-e2e-timeout');
|
|
}
|
|
|
|
export async function monitorGoalWriteActionUntilSettled(pending) {
|
|
state.goal.monitoredWriteActionIds.add(pending.actionId);
|
|
const deadline = Date.now() + 120_000;
|
|
while (Date.now() < deadline) {
|
|
await assertGoalRuntimeCanProgress('goal-write-action-settle');
|
|
await assertGoalInitialMarkerAbsent(
|
|
'goal-write-after-confirm',
|
|
pending.actionId,
|
|
);
|
|
const records = await readOptionalJsonl(
|
|
path.join(state.projectRoot, '.agent/agent.db'),
|
|
);
|
|
const settled = records.some(
|
|
(record) =>
|
|
record.agentId === pending.agentId &&
|
|
record.runId === pending.runId &&
|
|
record.actionId === pending.actionId &&
|
|
record.tool === pending.tool &&
|
|
[
|
|
'agent.runtime.action_receipt',
|
|
'agent.runtime.tool_action.observed',
|
|
'agent.runtime.tool_observation',
|
|
].includes(record.recordType),
|
|
);
|
|
if (settled) {
|
|
await assertGoalInitialMarkerAbsent(
|
|
'goal-write-settled',
|
|
pending.actionId,
|
|
);
|
|
return;
|
|
}
|
|
await sleep(100);
|
|
}
|
|
throw codedError('goal-write-action-settle-timeout');
|
|
}
|
|
|
|
export async function validateGoalRuntimeEvidence() {
|
|
const persistence = await readGoalRuntimePersistence();
|
|
const {
|
|
taskSnapshot,
|
|
events,
|
|
agentDb,
|
|
conversations,
|
|
activity,
|
|
output,
|
|
runtimeState,
|
|
contextBundle,
|
|
goal,
|
|
pendingActions,
|
|
} = persistence;
|
|
assert(taskSnapshot.all.length > 0, 'goal-task-evidence-missing');
|
|
assert(events.length > 0, 'goal-event-evidence-missing');
|
|
assert(agentDb.length > 0, 'goal-agent-db-evidence-missing');
|
|
assertNoPersistedImagePayload('goal-task', taskSnapshot.all);
|
|
assertNoPersistedImagePayload('goal-event', events);
|
|
assertNoPersistedImagePayload('goal-agent-db', agentDb);
|
|
|
|
const plan = inspectStructuredPlanSnapshot(runtimeState, 'goal-final-plan');
|
|
const verificationExecution =
|
|
findFinalSuccessfulGoalVerificationExecution(agentDb);
|
|
const projectRevision = await readJson(
|
|
path.join(state.projectRoot, '.agent/runtime/project-revision.json'),
|
|
);
|
|
const verificationGate = await readGoalVerificationGate(
|
|
projectRevision,
|
|
verificationExecution,
|
|
goal,
|
|
);
|
|
const expectedCompletionEvidence = [
|
|
`goalRevision=${state.goal.editedRevision}`,
|
|
`planRevision=${runtimeState.planRevision} completedSteps=${plan.completedStepHashes.length}`,
|
|
`verificationRequired=true verifiedRevision=${projectRevision.revision}`,
|
|
`runId=${state.initialRunId} sessionId=${state.initialSessionId}`,
|
|
];
|
|
assert(
|
|
plan.incompleteStepCount === 0 &&
|
|
plan.completedStepHashes.length === runtimeState.planSteps.length &&
|
|
state.goal.initialCompletedStepHashes.every((stepHash) =>
|
|
plan.completedStepHashes.includes(stepHash),
|
|
),
|
|
'goal-final-plan-incomplete',
|
|
);
|
|
assertGoalContextSnapshot(runtimeState, contextBundle, goal, 'goal-final');
|
|
assertStructuredPlanAuditSnapshot(
|
|
runtimeState,
|
|
agentDb,
|
|
'goal-final-plan-audit',
|
|
);
|
|
assert(
|
|
goal.schemaVersion === 'game-creator-agent-goal.v1' &&
|
|
goal.goalId === state.goal.goalId &&
|
|
goal.agentId === mainAgentId &&
|
|
goal.sessionId === state.initialSessionId &&
|
|
goal.runId === state.initialRunId &&
|
|
goal.revision === state.goal.editedRevision &&
|
|
goalSnapshotFingerprint(goal) ===
|
|
state.goal.editedGoalSnapshotFingerprint &&
|
|
state.goal.editedGoalSnapshotFingerprint !==
|
|
state.goal.initialGoalSnapshotFingerprint &&
|
|
goal.status === 'completed' &&
|
|
Number.isSafeInteger(goal.completedAt) &&
|
|
goal.completedAt > 0 &&
|
|
projectRevision.updatedAt <= goal.completedAt &&
|
|
verificationGate.updatedAt <= goal.completedAt &&
|
|
/^[0-9a-f]{64}$/u.test(goal.responseFingerprint) &&
|
|
Array.isArray(goal.completionEvidence) &&
|
|
JSON.stringify(goal.completionEvidence) ===
|
|
JSON.stringify(expectedCompletionEvidence) &&
|
|
runtimeState.status === 'idle' &&
|
|
runtimeState.phase === 'completed' &&
|
|
runtimeState.goalId === goal.goalId &&
|
|
runtimeState.goalRevision === goal.revision &&
|
|
runtimeState.goalStatus === 'completed',
|
|
'goal-final-state-invalid',
|
|
);
|
|
|
|
const targetTasks = taskSnapshot.all.filter(
|
|
(task) => task.agentId === mainAgentId && task.runId === state.initialRunId,
|
|
);
|
|
const targetRunIds = [
|
|
...new Set(
|
|
taskSnapshot.all
|
|
.filter((task) => task.agentId === mainAgentId)
|
|
.map((task) => task.runId),
|
|
),
|
|
].sort();
|
|
const completedTasks = targetTasks.filter(
|
|
(task) => task.status === 'completed' && task.phase === 'completed',
|
|
);
|
|
const latest = taskSnapshot.latest.find(
|
|
(task) => task.agentId === mainAgentId && task.runId === state.initialRunId,
|
|
);
|
|
assert(
|
|
JSON.stringify(targetRunIds) === JSON.stringify([state.initialRunId]) &&
|
|
completedTasks.length === 2 &&
|
|
completedTasks.filter((task) => task.goalStatus === 'active').length ===
|
|
1 &&
|
|
completedTasks.filter((task) => task.goalStatus === 'completed')
|
|
.length === 1 &&
|
|
latest?.goalStatus === 'completed',
|
|
'goal-finalization-projection-invalid',
|
|
);
|
|
|
|
const finalMessage = finalMessageId(
|
|
mainAgentId,
|
|
state.initialSessionId,
|
|
state.initialRunId,
|
|
);
|
|
const userMessages = conversations.filter(
|
|
(message) => message.role === 'user',
|
|
);
|
|
const assistantMessages = conversations.filter(
|
|
(message) => message.role === 'assistant',
|
|
);
|
|
const finalAssistant = assistantMessages.find(
|
|
(message) => message.messageId === finalMessage,
|
|
);
|
|
const assistantAudits = agentDb.filter(
|
|
(record) =>
|
|
record.recordType === 'conversation.message' &&
|
|
record.role === 'assistant' &&
|
|
record.agentId === mainAgentId &&
|
|
record.sessionId === state.initialSessionId &&
|
|
record.messageId === finalMessage,
|
|
);
|
|
const responseEvents = events.filter(
|
|
(event) =>
|
|
event.agentId === mainAgentId &&
|
|
event.runId === state.initialRunId &&
|
|
event.eventType === 'response' &&
|
|
event.phase === 'completed',
|
|
);
|
|
const completedEvents = events.filter(
|
|
(event) =>
|
|
event.agentId === mainAgentId &&
|
|
event.runId === state.initialRunId &&
|
|
event.eventType === 'turn.completed' &&
|
|
event.phase === 'completed',
|
|
);
|
|
assert(
|
|
userMessages.length === 2 &&
|
|
assistantMessages.length === 1 &&
|
|
finalAssistant?.agentId === mainAgentId &&
|
|
hashValue(finalAssistant.content.trim()) === goal.responseFingerprint &&
|
|
assistantAudits.length === 1 &&
|
|
responseEvents.length === 1 &&
|
|
completedEvents.length === 1,
|
|
'goal-final-assistant-invalid',
|
|
);
|
|
const providerLifecycle = validateGoalProviderRequestLifecycle(agentDb);
|
|
const finalizationLifecycle = validateGoalFinalizationLifecycle(
|
|
agentDb,
|
|
goal,
|
|
runtimeState,
|
|
);
|
|
assert(
|
|
verificationExecution.completionIndex <
|
|
finalizationLifecycle.firstStageIndex,
|
|
'goal-completed-before-verification-gate-passed',
|
|
);
|
|
|
|
const oldExecution = goalOldActionExecutionEvidence(
|
|
agentDb,
|
|
state.goal.initialPending.actionId,
|
|
);
|
|
const editedActionRecords = agentDb.filter(
|
|
(record) => record.actionId === state.goal.editedPending.actionId,
|
|
);
|
|
const editedExecutionCount = editedActionRecords.filter((record) =>
|
|
[
|
|
'agent.runtime.tool_action.executing',
|
|
'agent.runtime.tool_confirmation.approved',
|
|
].includes(record.recordType),
|
|
).length;
|
|
const editedReceipts = editedActionRecords.filter(
|
|
(record) =>
|
|
record.recordType === 'agent.runtime.action_receipt' &&
|
|
record.status === 'ok',
|
|
);
|
|
assert(
|
|
state.goal.initialPending.blockedObservationCount >= 1 &&
|
|
state.goal.initialPending.blockedReceiptCount === 1 &&
|
|
oldExecution.executionCount === 0 &&
|
|
oldExecution.successfulReceiptCount === 0 &&
|
|
editedExecutionCount >= 1 &&
|
|
editedReceipts.length === 1 &&
|
|
pendingActions.length === 0,
|
|
'goal-action-transition-invalid',
|
|
);
|
|
const executedGoalWriteActionIds = new Set(
|
|
agentDb
|
|
.filter(
|
|
(record) =>
|
|
record.agentId === mainAgentId &&
|
|
record.runId === state.initialRunId &&
|
|
goalProjectWriteTools.has(record.tool) &&
|
|
[
|
|
'agent.runtime.tool_action.executing',
|
|
'agent.runtime.tool_confirmation.approved',
|
|
].includes(record.recordType),
|
|
)
|
|
.map((record) => record.actionId)
|
|
.filter(isNonEmptyString),
|
|
);
|
|
assert(
|
|
[...executedGoalWriteActionIds].every((actionId) =>
|
|
state.goal.monitoredWriteActionIds.has(actionId),
|
|
) && state.goal.initialMarkerAbsenceCheckCount > 0,
|
|
'goal-write-action-marker-monitoring-incomplete',
|
|
);
|
|
assert(
|
|
state.goal.runner.pidfdClaimCount >= 2 &&
|
|
state.goal.runner.pidfdSignalCount >= 1,
|
|
'goal-runner-pidfd-evidence-invalid',
|
|
);
|
|
|
|
const repairEvidence = validateGoalRevisionTwoRepairEvidence(
|
|
agentDb,
|
|
verificationExecution,
|
|
);
|
|
const verification = await runProcess(process.execPath, ['verify-e2e.mjs'], {
|
|
cwd: state.projectRoot,
|
|
timeoutMs: 120_000,
|
|
});
|
|
assert(
|
|
verification.stdout.includes(commandPassedMarker),
|
|
'goal-final-project-verification-failed',
|
|
);
|
|
const goalDelivery = await fs.readFile(
|
|
path.join(state.projectRoot, goalDeliveryPath),
|
|
'utf8',
|
|
);
|
|
const oldMarkerProjectCount =
|
|
await countMarkerOutsideRuntimeControl(goalInitialMarker);
|
|
assert(
|
|
goalDelivery === `${goalFinalMarker}\n` && oldMarkerProjectCount === 0,
|
|
'goal-final-delivery-invalid',
|
|
);
|
|
|
|
const finalizationFiles = await listFiles(
|
|
path.join(state.projectRoot, '.agent/runtime/finalizations'),
|
|
);
|
|
const finalizationJournalCount = finalizationFiles.filter((file) =>
|
|
file.endsWith('.json'),
|
|
).length;
|
|
assert(finalizationJournalCount === 0, 'goal-finalization-journal-present');
|
|
|
|
const replayEvidence = validateToolActionReplays(agentDb);
|
|
const duplicateActionCount = duplicateCount(
|
|
agentDb.filter((record) => record.actionId).map(actionAuditIdentity),
|
|
);
|
|
const duplicateMessageCount = duplicateCount(
|
|
conversations.map((message) => message.messageId).filter(Boolean),
|
|
);
|
|
const receipts = agentDb.filter(
|
|
(record) => record.recordType === 'agent.runtime.action_receipt',
|
|
);
|
|
const duplicateReceiptCount = duplicateCount(
|
|
receipts.map(receiptAuditIdentity),
|
|
);
|
|
assert(
|
|
duplicateActionCount === 0 &&
|
|
duplicateMessageCount === 0 &&
|
|
duplicateReceiptCount === 0 &&
|
|
replayEvidence.sideEffectReplayCount === 0,
|
|
'goal-duplicate-or-replay-detected',
|
|
);
|
|
|
|
const publicSurfaces = {
|
|
event: events,
|
|
agentDb,
|
|
receipt: receipts,
|
|
activity,
|
|
output,
|
|
};
|
|
const goalPublicBodyLeakCounts = {};
|
|
for (const [surface, records] of Object.entries(publicSurfaces)) {
|
|
goalPublicBodyLeakCounts[surface] = countExactSecrets(
|
|
Buffer.from(JSON.stringify(records)),
|
|
goalPublicBodyValues(),
|
|
);
|
|
assert(
|
|
goalPublicBodyLeakCounts[surface] === 0,
|
|
`goal-body-public-${surface}-leak-detected`,
|
|
);
|
|
}
|
|
const goalPublicBodyLeakCount = sumObjectValues(goalPublicBodyLeakCounts);
|
|
const projectPathPublicLeakCounts = validateProjectRootPublicLeakBoundary(
|
|
publicSurfaces,
|
|
'goal-public',
|
|
);
|
|
const projectPathPublicLeakCount = sumObjectValues(
|
|
projectPathPublicLeakCounts,
|
|
);
|
|
state.lureLeakCount = await countLureLeaks();
|
|
assert(state.lureLeakCount === 0, 'sensitive-lure-leak-detected');
|
|
|
|
const nativeToolPlanProtocolEvidence =
|
|
validateNativeRuntimeToolPlanProtocolEvidence(agentDb);
|
|
const successfulToolExecutionCount = receipts.filter(
|
|
(record) =>
|
|
record.agentId === mainAgentId &&
|
|
record.runId === state.initialRunId &&
|
|
record.status === 'ok',
|
|
).length;
|
|
return {
|
|
scenario: 'goal-edit-pause-runner-restart-resume',
|
|
taskCount: taskSnapshot.all.length,
|
|
eventCount: events.length,
|
|
agentDbRecordCount: agentDb.length,
|
|
conversationMessageCount: conversations.length,
|
|
successfulToolExecutionCount,
|
|
...nativeToolPlanProtocolEvidence,
|
|
structuredPlanRevision: plan.revision,
|
|
structuredPlanCompletedStepCount: plan.completedStepHashes.length,
|
|
goalInitialCompletedStepCount: state.goal.initialCompletedStepHashes.length,
|
|
goalRetainedInitialCompletedStepCount:
|
|
state.goal.initialCompletedStepHashes.filter((stepHash) =>
|
|
plan.completedStepHashes.includes(stepHash),
|
|
).length,
|
|
goalEditedEvidenceAbsentBeforeEdit:
|
|
state.goal.editedEvidenceAbsentBeforeEdit,
|
|
goalRevisionOneFixtureIsolated: state.goal.revisionOneFixtureIsolated,
|
|
goalRevisionTwoFixtureInjected: state.goal.revisionTwoFixtureInjected,
|
|
goalRevisionTwoHostFailureObserved:
|
|
state.goal.revisionTwoHostFailureObserved,
|
|
goalRevisionTwoFailureObserved: state.goal.revisionTwoFailureObserved,
|
|
goalRevisionTwoFailureExitCode: state.goal.revisionTwoFailureExitCode,
|
|
goalRevisionTwoFailureFingerprint: state.goal.revisionTwoFailureFingerprint,
|
|
goalRevisionTwoRepairActionCount: repairEvidence.actionCount,
|
|
goalRevisionTwoRepairFileWriteCount: repairEvidence.fileWriteCount,
|
|
goalRevisionTwoRepairPatchsetCount: repairEvidence.patchsetCount,
|
|
goalIdHash: hashValue(goal.goalId),
|
|
goalInitialRevision: state.goal.initialRevision,
|
|
goalEditedRevision: state.goal.editedRevision,
|
|
goalSnapshotFingerprintChanged:
|
|
state.goal.initialGoalSnapshotFingerprint !==
|
|
state.goal.editedGoalSnapshotFingerprint,
|
|
goalInitialMarkerAbsenceCheckCount:
|
|
state.goal.initialMarkerAbsenceCheckCount,
|
|
goalMonitoredWriteActionCount: state.goal.monitoredWriteActionIds.size,
|
|
goalPreFailureDeliveryActionCount:
|
|
state.goal.preFailureDeliveryActionIds.size,
|
|
goalFinalStatus: goal.status,
|
|
goalEditProviderInterrupted: state.goal.editProviderInterrupted,
|
|
goalPauseProviderInterrupted: state.goal.pauseProviderInterrupted,
|
|
goalPausedBeforeKill: true,
|
|
goalPausedAfterRestart: true,
|
|
goalRunnerBootChanged:
|
|
state.goal.oldRunnerBootId !== state.goal.newRunnerBootId,
|
|
goalRunnerKillMethod: 'linux-pidfd',
|
|
goalRunnerPidfdClaimCount: state.goal.runner.pidfdClaimCount,
|
|
goalRunnerPidfdSignalCount: state.goal.runner.pidfdSignalCount,
|
|
goalExecutionOwnerRecovered: state.goal.executionOwnerRecovered === true,
|
|
goalExplicitResumeSameRun: true,
|
|
goalTargetRunCount: targetRunIds.length,
|
|
goalUnexpectedRunCount: targetRunIds.length - 1,
|
|
goalOldActionCount: 1,
|
|
goalOldActionBlockedReceiptCount:
|
|
state.goal.initialPending.blockedReceiptCount,
|
|
goalOldActionExecutionCount: oldExecution.executionCount,
|
|
goalOldActionReplayCount: oldExecution.successfulReceiptCount,
|
|
goalEditedActionExecutionCount: editedExecutionCount,
|
|
goalPausedTaskDelta:
|
|
state.goal.pausedAfterRestart.taskCount -
|
|
state.goal.pauseSnapshot.taskCount,
|
|
goalPausedPlanDelta:
|
|
state.goal.pausedAfterRestart.planRevision -
|
|
state.goal.pauseSnapshot.planRevision,
|
|
goalPausedConversationDelta:
|
|
state.goal.pausedAfterRestart.conversationCount -
|
|
state.goal.pauseSnapshot.conversationCount,
|
|
goalPausedProviderPlanDelta:
|
|
state.goal.pausedAfterRestart.planProtocolCount -
|
|
state.goal.pauseSnapshot.planProtocolCount,
|
|
goalPausedProviderRequestStartedDelta:
|
|
state.goal.pausedAfterRestart.providerRequestStartedCount -
|
|
state.goal.pauseSnapshot.providerRequestStartedCount,
|
|
goalPausedActionProgressDelta:
|
|
state.goal.pausedAfterRestart.actionProgressCount -
|
|
state.goal.pauseSnapshot.actionProgressCount,
|
|
goalContextSchemaVersion: contextBundle.schemaVersion,
|
|
goalPendingSchemaVersion: state.goal.editedPending.schemaVersion,
|
|
projectRevision: projectRevision.revision,
|
|
verificationPassed: true,
|
|
verificationActionIdentityBound: true,
|
|
verificationActionIdHash: hashValue(verificationExecution.actionId),
|
|
goalProviderRequestStartedCount: providerLifecycle.startedCount,
|
|
goalProviderRequestTerminalCount: providerLifecycle.terminalCount,
|
|
goalFinalizationSchemaVersion: finalizationLifecycle.schemaVersion,
|
|
goalFinalizationObserved: true,
|
|
goalFinalizationStageCount: finalizationLifecycle.stageCount,
|
|
goalFinalizationIdHash: finalizationLifecycle.finalizationIdHash,
|
|
goalFinalizationJournalCount: finalizationJournalCount,
|
|
goalCompletedProjectionCount: completedTasks.length,
|
|
goalAssistantCount: assistantMessages.length,
|
|
goalPublicBodyLeakCount,
|
|
goalBodyReportLeakCount: state.goalBodyReportLeakCount,
|
|
sideEffectActionCount: replayEvidence.sideEffectActionCount,
|
|
sideEffectReplayCount: replayEvidence.sideEffectReplayCount,
|
|
idempotentReplayActionCount: replayEvidence.idempotentReplayActionCount,
|
|
actionReceiptReplayRecordCount:
|
|
replayEvidence.actionReceiptReplayRecordCount,
|
|
finalAssistantCount: assistantMessages.length,
|
|
finalAssistantAuditCount: assistantAudits.length,
|
|
duplicateActionCount,
|
|
duplicateMessageCount,
|
|
duplicateReceiptCount,
|
|
confirmedActionCount: state.confirmedActionIds.size,
|
|
projectPathPublicLeakCount,
|
|
projectPathPublicSurfaceCount: Object.keys(projectPathPublicLeakCounts)
|
|
.length,
|
|
secretLeakCount: state.transcriptLeakCount + state.projectLeakCount,
|
|
lureLeakCount: state.lureLeakCount,
|
|
paths: [
|
|
'.agent/runtime/tasks',
|
|
'.agent/runtime/events',
|
|
'.agent/agent.db',
|
|
relativeProjectPath(mainContextBundlePath()),
|
|
relativeProjectPath(mainRuntimeStatePath()),
|
|
'.agent/runtime/goals/current',
|
|
'.agent/conversations',
|
|
goalDeliveryPath,
|
|
],
|
|
};
|
|
}
|
|
|
|
export async function readGoalVerificationGate(
|
|
projectRevision,
|
|
verificationExecution,
|
|
goal,
|
|
) {
|
|
const manifest = await readJson(
|
|
path.join(state.projectRoot, '.agent/manifest.json'),
|
|
);
|
|
assert(
|
|
projectRevision?.schemaVersion === 'game-creator-project-revision.v1' &&
|
|
isNonEmptyString(projectRevision.projectId) &&
|
|
manifest?.projectId === projectRevision.projectId &&
|
|
goal.projectId === manifest.projectId &&
|
|
Number.isSafeInteger(projectRevision.revision) &&
|
|
projectRevision.revision > 0 &&
|
|
Number.isSafeInteger(projectRevision.updatedAt) &&
|
|
projectRevision.updatedAt > 0,
|
|
'goal-project-revision-invalid',
|
|
);
|
|
const verificationFiles = await listFiles(
|
|
path.join(state.projectRoot, '.agent/runtime/verification'),
|
|
);
|
|
const matching = [];
|
|
for (const file of verificationFiles.filter((entry) =>
|
|
entry.endsWith('.json'),
|
|
)) {
|
|
const value = await readJson(file);
|
|
if (value.agentId === mainAgentId && value.runId === state.initialRunId) {
|
|
matching.push(value);
|
|
}
|
|
}
|
|
assert(matching.length === 1, 'goal-verification-gate-count-invalid');
|
|
const gate = matching[0];
|
|
assert(
|
|
gate.schemaVersion === 'game-creator-verification-gate.v1' &&
|
|
gate.projectId === projectRevision.projectId &&
|
|
gate.agentId === mainAgentId &&
|
|
gate.runId === state.initialRunId &&
|
|
gate.requiresVerification === true &&
|
|
gate.mutationRevision === projectRevision.revision &&
|
|
gate.verifiedRevision === projectRevision.revision &&
|
|
['file.write', 'project.patchset'].includes(gate.lastMutationTool) &&
|
|
gate.lastVerificationTool === verificationExecution.tool &&
|
|
gate.lastVerificationStatus === 'passed' &&
|
|
Number.isSafeInteger(gate.updatedAt) &&
|
|
gate.updatedAt >= projectRevision.updatedAt &&
|
|
gate.updatedAt >= verificationExecution.verificationAudit.updatedAt &&
|
|
verificationExecution.verificationAudit.actionId ===
|
|
verificationExecution.actionId &&
|
|
verificationExecution.verificationAudit.actionFingerprint ===
|
|
verificationExecution.actionFingerprint,
|
|
'goal-verification-credential-invalid',
|
|
);
|
|
return gate;
|
|
}
|
|
|
|
export function findFinalSuccessfulGoalVerificationExecution(agentDb) {
|
|
let auditIndex = -1;
|
|
for (let index = agentDb.length - 1; index >= 0; index -= 1) {
|
|
const record = agentDb[index];
|
|
if (
|
|
record.recordType === 'agent.runtime.project.verify' &&
|
|
record.agentId === mainAgentId &&
|
|
record.runId === state.initialRunId
|
|
) {
|
|
auditIndex = index;
|
|
break;
|
|
}
|
|
}
|
|
assert(auditIndex >= 0, 'goal-project-verification-missing');
|
|
const verificationAudit = agentDb[auditIndex];
|
|
assert(
|
|
verificationAudit.status === 'completed' &&
|
|
verificationAudit.exitCode === 0 &&
|
|
verificationAudit.timedOut === false &&
|
|
['test', 'check:e2e'].includes(verificationAudit.script) &&
|
|
verificationAudit.expectedCommand === verificationCommand &&
|
|
isNonEmptyString(verificationAudit.actionId) &&
|
|
isNonEmptyString(verificationAudit.actionFingerprint) &&
|
|
Number.isSafeInteger(verificationAudit.updatedAt) &&
|
|
hasExpectedWorkspaceSandboxMetadata(verificationAudit),
|
|
'goal-final-project-verification-audit-invalid',
|
|
);
|
|
const execution = findSuccessfulToolExecution(
|
|
agentDb,
|
|
'project.verify',
|
|
state.initialRunId,
|
|
(candidate) =>
|
|
candidate.actionId === verificationAudit.actionId &&
|
|
candidate.actionFingerprint === verificationAudit.actionFingerprint &&
|
|
['test', 'check:e2e'].includes(
|
|
auditInputValue(candidate.inputSummary, 'script'),
|
|
),
|
|
);
|
|
assert(
|
|
execution &&
|
|
execution.startIndex < auditIndex &&
|
|
auditIndex < execution.resultIndex,
|
|
'goal-final-project-verification-action-identity-invalid',
|
|
);
|
|
return { ...execution, auditIndex, verificationAudit };
|
|
}
|
|
|
|
export function validateGoalRevisionTwoRepairEvidence(
|
|
agentDb,
|
|
verificationExecution,
|
|
) {
|
|
assert(
|
|
state.goal.revisionTwoFixtureInjected === true &&
|
|
state.goal.revisionTwoHostFailureObserved === true &&
|
|
state.goal.revisionTwoFailureObserved === true &&
|
|
Number.isSafeInteger(state.goal.revisionTwoAgentDbBoundary) &&
|
|
state.goal.revisionTwoAgentDbBoundary > 0,
|
|
'goal-revision-two-failure-boundary-invalid',
|
|
);
|
|
const afterFailureBoundary = (execution) =>
|
|
execution.startIndex >= state.goal.revisionTwoAgentDbBoundary &&
|
|
execution.completionIndex < verificationExecution.startIndex;
|
|
const gamePatchset = findSuccessfulToolExecution(
|
|
agentDb,
|
|
'project.patchset',
|
|
state.initialRunId,
|
|
(execution) =>
|
|
afterFailureBoundary(execution) &&
|
|
auditPatchsetPathsInclude(execution.inputSummary, [
|
|
'update:game/index.html',
|
|
]),
|
|
);
|
|
const createdPatchset = findSuccessfulToolExecution(
|
|
agentDb,
|
|
'project.patchset',
|
|
state.initialRunId,
|
|
(execution) =>
|
|
afterFailureBoundary(execution) &&
|
|
auditPatchsetPathsInclude(execution.inputSummary, [
|
|
`create:${patchsetCreatedPath}`,
|
|
]),
|
|
);
|
|
const gameWrite = findSuccessfulToolExecution(
|
|
agentDb,
|
|
'file.write',
|
|
state.initialRunId,
|
|
(execution) =>
|
|
afterFailureBoundary(execution) &&
|
|
auditPathEquals(execution.inputSummary, 'game/index.html'),
|
|
);
|
|
const createdWrite = findSuccessfulToolExecution(
|
|
agentDb,
|
|
'file.write',
|
|
state.initialRunId,
|
|
(execution) =>
|
|
afterFailureBoundary(execution) &&
|
|
auditPathEquals(execution.inputSummary, patchsetCreatedPath),
|
|
);
|
|
assert(
|
|
Boolean(gamePatchset || gameWrite) &&
|
|
Boolean(createdPatchset || createdWrite),
|
|
'goal-revision-two-agent-repair-evidence-missing',
|
|
);
|
|
const executions = [
|
|
gamePatchset ?? gameWrite,
|
|
createdPatchset ?? createdWrite,
|
|
];
|
|
const byAction = new Map(
|
|
executions.map((execution) => [execution.actionId, execution]),
|
|
);
|
|
return {
|
|
actionCount: byAction.size,
|
|
fileWriteCount: [...byAction.values()].filter(
|
|
(execution) => execution.tool === 'file.write',
|
|
).length,
|
|
patchsetCount: [...byAction.values()].filter(
|
|
(execution) => execution.tool === 'project.patchset',
|
|
).length,
|
|
};
|
|
}
|
|
|
|
export function validateGoalProviderRequestLifecycle(agentDb) {
|
|
const records = agentDb.filter(
|
|
(record) =>
|
|
record.recordType === 'agent.runtime.provider_request.lifecycle' &&
|
|
record.agentId === mainAgentId &&
|
|
record.runId === state.initialRunId,
|
|
);
|
|
const byRequest = new Map();
|
|
for (const record of records) {
|
|
assert(
|
|
record.auditSchemaVersion === providerRequestLifecycleSchemaVersion &&
|
|
record.taskId &&
|
|
record.sessionId === state.initialSessionId &&
|
|
['tool-plan', 'final-reply'].includes(record.requestKind) &&
|
|
typeof record.requestSlot === 'string' &&
|
|
typeof record.webSearchEnabled === 'boolean' &&
|
|
(record.requestKind !== 'final-reply' ||
|
|
record.webSearchEnabled === false) &&
|
|
!['prompt', 'response', 'error', 'baseUrl', 'model'].some((key) =>
|
|
Object.hasOwn(record, key),
|
|
),
|
|
'goal-provider-request-lifecycle-invalid',
|
|
);
|
|
const group = byRequest.get(record.requestId) ?? [];
|
|
group.push(record);
|
|
byRequest.set(record.requestId, group);
|
|
}
|
|
assert(byRequest.size > 0, 'goal-provider-request-lifecycle-missing');
|
|
for (const group of byRequest.values()) {
|
|
assert(
|
|
group.length === 2 &&
|
|
group[0].status === 'started' &&
|
|
['completed', 'failed', 'interrupted'].includes(group[1].status) &&
|
|
group[0].requestKind === group[1].requestKind &&
|
|
group[0].requestSlot === group[1].requestSlot,
|
|
'goal-provider-request-lifecycle-incomplete',
|
|
);
|
|
}
|
|
return {
|
|
startedCount: byRequest.size,
|
|
terminalCount: byRequest.size,
|
|
};
|
|
}
|
|
|
|
export function validateGoalFinalizationLifecycle(agentDb, goal, runtimeState) {
|
|
const records = agentDb.filter(
|
|
(record) =>
|
|
record.recordType === 'agent.runtime.finalization.lifecycle' &&
|
|
record.agentId === mainAgentId &&
|
|
record.runId === state.initialRunId,
|
|
);
|
|
const expectedStages = [
|
|
'prepared',
|
|
'assistant-persisted',
|
|
'runtime-completed',
|
|
'goal-completed',
|
|
];
|
|
assert(
|
|
records.length === expectedStages.length,
|
|
'goal-finalization-audit-count-invalid',
|
|
);
|
|
const finalizationId = records[0]?.finalizationId;
|
|
const messageId = records[0]?.messageId;
|
|
const expectedGoalSnapshotFingerprint = goalSnapshotFingerprint(goal);
|
|
for (const [index, record] of records.entries()) {
|
|
assert(
|
|
record.auditSchemaVersion === 'game-creator-finalization-lifecycle.v1' &&
|
|
record.journalSchemaVersion ===
|
|
'game-creator-runtime-finalization.v4' &&
|
|
record.finalizationId === finalizationId &&
|
|
record.messageId === messageId &&
|
|
record.taskId === runtimeState.taskId &&
|
|
record.sessionId === state.initialSessionId &&
|
|
record.stage === expectedStages[index] &&
|
|
record.stageOrdinal === index + 1 &&
|
|
record.previousStage ===
|
|
(index === 0 ? null : expectedStages[index - 1]) &&
|
|
record.goalId === goal.goalId &&
|
|
record.goalRevision === goal.revision &&
|
|
record.goalSnapshotFingerprint === expectedGoalSnapshotFingerprint &&
|
|
record.planRevision === runtimeState.planRevision &&
|
|
record.responseFingerprint === goal.responseFingerprint &&
|
|
Number.isSafeInteger(record.stageAt) &&
|
|
record.stageAt > 0 &&
|
|
!['task', 'response', 'prompt', 'observation'].some((key) =>
|
|
Object.hasOwn(record, key),
|
|
),
|
|
'goal-finalization-audit-invalid',
|
|
);
|
|
if (index > 0) {
|
|
assert(
|
|
record.stageAt >= records[index - 1].stageAt,
|
|
'goal-finalization-audit-time-regressed',
|
|
);
|
|
}
|
|
}
|
|
const assistantAuditIndex = agentDb.findIndex(
|
|
(record) =>
|
|
record.recordType === 'conversation.message' &&
|
|
record.messageId === messageId &&
|
|
record.role === 'assistant',
|
|
);
|
|
const assistantStageIndex = agentDb.findIndex(
|
|
(record) =>
|
|
record.recordType === 'agent.runtime.finalization.lifecycle' &&
|
|
record.finalizationId === finalizationId &&
|
|
record.stage === 'assistant-persisted',
|
|
);
|
|
assert(
|
|
assistantAuditIndex >= 0 && assistantAuditIndex < assistantStageIndex,
|
|
'goal-finalization-assistant-order-invalid',
|
|
);
|
|
return {
|
|
schemaVersion: records[0].journalSchemaVersion,
|
|
stageCount: records.length,
|
|
finalizationIdHash: hashValue(finalizationId),
|
|
firstStageIndex: agentDb.findIndex((record) => record === records[0]),
|
|
};
|
|
}
|
|
|
|
export async function assertGoalInitialMarkerAbsent(code, actionId = null) {
|
|
if (!isGoalRuntimeSuite() || !state.projectRoot) return;
|
|
const count = await countMarkerOutsideRuntimeControl(goalInitialMarker);
|
|
state.goal.initialMarkerAbsenceCheckCount += 1;
|
|
if (isNonEmptyString(actionId)) {
|
|
state.goal.monitoredWriteActionIds.add(actionId);
|
|
}
|
|
assert(count === 0, `${code}-revision-one-marker-landed`);
|
|
}
|
|
|
|
export function emptyGoalEvidence() {
|
|
return {
|
|
scenario: 'goal-edit-pause-runner-restart-resume',
|
|
taskCount: 0,
|
|
eventCount: 0,
|
|
agentDbRecordCount: 0,
|
|
conversationMessageCount: 0,
|
|
successfulToolExecutionCount: 0,
|
|
toolPlanProtocolCount: 0,
|
|
nativeRuntimeToolPlanCount: 0,
|
|
toolPlanRepairCount: 0,
|
|
nativeRuntimeToolPlanRepairCount: 0,
|
|
toolPlanRepairedLoopCount: 0,
|
|
toolPlanSecondRepairCount: 0,
|
|
toolPlanRepairCountsByProtocolErrorKind:
|
|
emptyToolPlanRepairCountsByProtocolErrorKind(),
|
|
wrapperToolPlanFallbackCount: 0,
|
|
textJsonToolPlanFallbackCount: 0,
|
|
toolPlanAuditPayloadLeakCount: 0,
|
|
structuredPlanRevision: 0,
|
|
structuredPlanCompletedStepCount: 0,
|
|
goalInitialCompletedStepCount: 0,
|
|
goalRetainedInitialCompletedStepCount: 0,
|
|
goalEditedEvidenceAbsentBeforeEdit: false,
|
|
goalRevisionOneFixtureIsolated: false,
|
|
goalRevisionTwoFixtureInjected: false,
|
|
goalRevisionTwoHostFailureObserved: false,
|
|
goalRevisionTwoFailureObserved: false,
|
|
goalRevisionTwoFailureExitCode: null,
|
|
goalRevisionTwoFailureFingerprint: null,
|
|
goalRevisionTwoRepairActionCount: 0,
|
|
goalRevisionTwoRepairFileWriteCount: 0,
|
|
goalRevisionTwoRepairPatchsetCount: 0,
|
|
goalIdHash: null,
|
|
goalInitialRevision: 0,
|
|
goalEditedRevision: 0,
|
|
goalSnapshotFingerprintChanged: false,
|
|
goalInitialMarkerAbsenceCheckCount: 0,
|
|
goalMonitoredWriteActionCount: 0,
|
|
goalPreFailureDeliveryActionCount: 0,
|
|
goalFinalStatus: null,
|
|
goalEditProviderInterrupted: false,
|
|
goalPauseProviderInterrupted: false,
|
|
goalPausedBeforeKill: false,
|
|
goalPausedAfterRestart: false,
|
|
goalRunnerBootChanged: false,
|
|
goalRunnerKillMethod: null,
|
|
goalRunnerPidfdClaimCount: 0,
|
|
goalRunnerPidfdSignalCount: 0,
|
|
goalRunnerStopped: false,
|
|
goalAppDataCleanupPerformed: false,
|
|
goalExecutionOwnerRecovered: false,
|
|
goalExplicitResumeSameRun: false,
|
|
goalTargetRunCount: 0,
|
|
goalUnexpectedRunCount: 0,
|
|
goalOldActionCount: 0,
|
|
goalOldActionBlockedReceiptCount: 0,
|
|
goalOldActionExecutionCount: 0,
|
|
goalOldActionReplayCount: 0,
|
|
goalEditedActionExecutionCount: 0,
|
|
goalPausedTaskDelta: 0,
|
|
goalPausedPlanDelta: 0,
|
|
goalPausedConversationDelta: 0,
|
|
goalPausedProviderPlanDelta: 0,
|
|
goalPausedProviderRequestStartedDelta: 0,
|
|
goalPausedActionProgressDelta: 0,
|
|
goalContextSchemaVersion: null,
|
|
goalPendingSchemaVersion: null,
|
|
projectRevision: 0,
|
|
verificationPassed: false,
|
|
verificationActionIdentityBound: false,
|
|
verificationActionIdHash: null,
|
|
goalProviderRequestStartedCount: 0,
|
|
goalProviderRequestTerminalCount: 0,
|
|
goalFinalizationSchemaVersion: null,
|
|
goalFinalizationObserved: false,
|
|
goalFinalizationStageCount: 0,
|
|
goalFinalizationIdHash: null,
|
|
goalFinalizationJournalCount: 0,
|
|
goalCompletedProjectionCount: 0,
|
|
goalAssistantCount: 0,
|
|
goalPublicBodyLeakCount: 0,
|
|
goalBodyReportLeakCount: 0,
|
|
sideEffectActionCount: 0,
|
|
sideEffectReplayCount: 0,
|
|
idempotentReplayActionCount: 0,
|
|
actionReceiptReplayRecordCount: 0,
|
|
finalAssistantCount: 0,
|
|
finalAssistantAuditCount: 0,
|
|
duplicateActionCount: 0,
|
|
duplicateMessageCount: 0,
|
|
duplicateReceiptCount: 0,
|
|
confirmedActionCount: 0,
|
|
projectPathPublicLeakCount: 0,
|
|
projectPathPublicSurfaceCount: 0,
|
|
projectPathTranscriptLeakCount: 0,
|
|
projectPathReportLeakCount: 0,
|
|
secretLeakCount: 0,
|
|
lureLeakCount: 0,
|
|
failureEvidenceErrors: {
|
|
task: [],
|
|
event: [],
|
|
agentDb: [],
|
|
conversation: [],
|
|
},
|
|
paths: [],
|
|
};
|
|
}
|
|
|
|
export async function collectPartialGoalEvidence() {
|
|
const [taskSurface, eventSurface, agentDbSurface, conversationSurface] =
|
|
await Promise.all([
|
|
collectPartialRuntimeJsonlSurface('goal', 'task', async () =>
|
|
(
|
|
await listFiles(path.join(state.projectRoot, '.agent/runtime/tasks'))
|
|
).filter((file) => file.endsWith('.jsonl')),
|
|
),
|
|
collectPartialRuntimeJsonlSurface('goal', 'event', async () =>
|
|
(
|
|
await listFiles(path.join(state.projectRoot, '.agent/runtime/events'))
|
|
).filter((file) => file.endsWith('.jsonl')),
|
|
),
|
|
collectPartialRuntimeJsonlSurface('goal', 'agent-db', async () => [
|
|
path.join(state.projectRoot, '.agent/agent.db'),
|
|
]),
|
|
collectPartialRuntimeJsonlSurface('goal', 'conversation', async () =>
|
|
(
|
|
await listFiles(path.join(state.projectRoot, '.agent/conversations'))
|
|
).filter((file) => file.endsWith('.jsonl')),
|
|
),
|
|
]);
|
|
const taskSnapshot = buildTaskSnapshot(taskSurface.records);
|
|
const events = eventSurface.records;
|
|
const agentDb = agentDbSurface.records;
|
|
const conversations = conversationSurface.records;
|
|
const runtime = await readJson(mainRuntimeStatePath()).catch(() => null);
|
|
const contextFiles = await listFiles(
|
|
path.join(state.projectRoot, '.agent/runtime/context-bundles', mainAgentId),
|
|
).catch(() => []);
|
|
const context = await readLatestJsonFile(contextFiles);
|
|
const goalFiles = await listFiles(
|
|
path.join(state.projectRoot, '.agent/runtime/goals/current'),
|
|
).catch(() => []);
|
|
const goals = await Promise.all(
|
|
goalFiles
|
|
.filter((file) => file.endsWith('.json'))
|
|
.map((file) => readJson(file).catch(() => null)),
|
|
);
|
|
const goal = goals.find(
|
|
(candidate) =>
|
|
candidate?.agentId === mainAgentId &&
|
|
(!state.initialRunId || candidate.runId === state.initialRunId),
|
|
);
|
|
const targetRunIds = [
|
|
...new Set(
|
|
taskSnapshot.all
|
|
.filter((task) => task.agentId === mainAgentId)
|
|
.map((task) => task.runId),
|
|
),
|
|
];
|
|
const receipts = agentDb.filter(
|
|
(record) => record.recordType === 'agent.runtime.action_receipt',
|
|
);
|
|
const providerLifecycle = agentDb.filter(
|
|
(record) =>
|
|
record.recordType === 'agent.runtime.provider_request.lifecycle' &&
|
|
record.agentId === mainAgentId,
|
|
);
|
|
const finalizationLifecycle = agentDb.filter(
|
|
(record) =>
|
|
record.recordType === 'agent.runtime.finalization.lifecycle' &&
|
|
record.agentId === mainAgentId,
|
|
);
|
|
const completedStepCount = Array.isArray(runtime?.planSteps)
|
|
? runtime.planSteps.filter((step) => step.status === 'completed').length
|
|
: 0;
|
|
const nativeToolPlanProtocolEvidence =
|
|
collectNativeRuntimeToolPlanProtocolEvidence(agentDb);
|
|
return {
|
|
taskCount: taskSnapshot.all.length,
|
|
eventCount: events.length,
|
|
agentDbRecordCount: agentDb.length,
|
|
conversationMessageCount: conversations.length,
|
|
successfulToolExecutionCount: receipts.filter(
|
|
(record) => record.status === 'ok',
|
|
).length,
|
|
...nativeToolPlanProtocolEvidence,
|
|
structuredPlanRevision: runtime?.planRevision ?? 0,
|
|
structuredPlanCompletedStepCount: completedStepCount,
|
|
goalEditedEvidenceAbsentBeforeEdit:
|
|
state.goal.editedEvidenceAbsentBeforeEdit,
|
|
goalRevisionOneFixtureIsolated: state.goal.revisionOneFixtureIsolated,
|
|
goalRevisionTwoFixtureInjected: state.goal.revisionTwoFixtureInjected,
|
|
goalRevisionTwoHostFailureObserved:
|
|
state.goal.revisionTwoHostFailureObserved,
|
|
goalRevisionTwoFailureObserved: state.goal.revisionTwoFailureObserved,
|
|
goalRevisionTwoFailureExitCode: state.goal.revisionTwoFailureExitCode,
|
|
goalRevisionTwoFailureFingerprint: state.goal.revisionTwoFailureFingerprint,
|
|
goalIdHash: isNonEmptyString(goal?.goalId) ? hashValue(goal.goalId) : null,
|
|
goalInitialRevision: state.goal.initialRevision || goal?.revision || 0,
|
|
goalEditedRevision: state.goal.editedRevision,
|
|
goalSnapshotFingerprintChanged:
|
|
isNonEmptyString(state.goal.initialGoalSnapshotFingerprint) &&
|
|
isNonEmptyString(state.goal.editedGoalSnapshotFingerprint) &&
|
|
state.goal.initialGoalSnapshotFingerprint !==
|
|
state.goal.editedGoalSnapshotFingerprint,
|
|
goalInitialMarkerAbsenceCheckCount:
|
|
state.goal.initialMarkerAbsenceCheckCount,
|
|
goalMonitoredWriteActionCount: state.goal.monitoredWriteActionIds.size,
|
|
goalPreFailureDeliveryActionCount:
|
|
state.goal.preFailureDeliveryActionIds.size,
|
|
goalFinalStatus: goal?.status ?? runtime?.goalStatus ?? null,
|
|
goalRunnerKillMethod:
|
|
state.goal.runner.pidfdClaimCount > 0 ? 'linux-pidfd' : null,
|
|
goalRunnerPidfdClaimCount: state.goal.runner.pidfdClaimCount,
|
|
goalRunnerPidfdSignalCount: state.goal.runner.pidfdSignalCount,
|
|
goalTargetRunCount: targetRunIds.length,
|
|
goalUnexpectedRunCount: Math.max(0, targetRunIds.length - 1),
|
|
goalContextSchemaVersion: context?.schemaVersion ?? null,
|
|
goalProviderRequestStartedCount: providerLifecycle.filter(
|
|
(record) => record.status === 'started',
|
|
).length,
|
|
goalProviderRequestTerminalCount: providerLifecycle.filter((record) =>
|
|
['completed', 'failed', 'interrupted'].includes(record.status),
|
|
).length,
|
|
goalFinalizationSchemaVersion:
|
|
finalizationLifecycle.at(-1)?.journalSchemaVersion ?? null,
|
|
goalFinalizationObserved: finalizationLifecycle.length > 0,
|
|
goalFinalizationStageCount: finalizationLifecycle.length,
|
|
goalFinalizationIdHash: isNonEmptyString(
|
|
finalizationLifecycle.at(-1)?.finalizationId,
|
|
)
|
|
? hashValue(finalizationLifecycle.at(-1).finalizationId)
|
|
: null,
|
|
goalAssistantCount: conversations.filter(
|
|
(message) => message.role === 'assistant',
|
|
).length,
|
|
finalAssistantCount:
|
|
goal?.status === 'completed'
|
|
? conversations.filter((message) => message.role === 'assistant').length
|
|
: 0,
|
|
finalAssistantAuditCount:
|
|
goal?.status === 'completed'
|
|
? agentDb.filter(
|
|
(record) =>
|
|
record.recordType === 'conversation.message' &&
|
|
record.role === 'assistant',
|
|
).length
|
|
: 0,
|
|
duplicateMessageCount: duplicateCount(
|
|
conversations.map((message) => message.messageId).filter(Boolean),
|
|
),
|
|
duplicateReceiptCount: duplicateCount(receipts.map(receiptAuditIdentity)),
|
|
failureEvidenceErrors: {
|
|
task: taskSurface.errors,
|
|
event: eventSurface.errors,
|
|
agentDb: agentDbSurface.errors,
|
|
conversation: conversationSurface.errors,
|
|
},
|
|
paths: [
|
|
'.agent/runtime/tasks',
|
|
'.agent/runtime/events',
|
|
'.agent/agent.db',
|
|
'.agent/runtime/context-bundles',
|
|
'.agent/runtime/goals/current',
|
|
'.agent/conversations',
|
|
],
|
|
};
|
|
}
|
|
|
|
export function isGoalRuntimeSuite() {
|
|
return state.suite === goalRuntimeSuite;
|
|
}
|
|
|
|
export function goalStaleActionReceiptMatchesOriginalAction(original, receipt) {
|
|
if (
|
|
!isGoalRuntimeSuite() ||
|
|
receipt.recordType !== 'agent.runtime.action_receipt' ||
|
|
receipt.tool !== 'runtime.goal' ||
|
|
receipt.status !== 'blocked' ||
|
|
!goalProjectWriteTools.has(original.tool) ||
|
|
!isNonEmptyString(original.rawInputSummary) ||
|
|
original.agentId !== receipt.agentId ||
|
|
original.runId !== receipt.runId ||
|
|
original.actionFingerprint !== receipt.actionFingerprint
|
|
) {
|
|
return false;
|
|
}
|
|
const expectedReceiptSummary = canonicalAuditInputSummary(
|
|
`inputSummarySha256=${hashValue(original.rawInputSummary)} · inputSummaryChars=${[...original.rawInputSummary].length}`,
|
|
);
|
|
return receipt.inputSummary === expectedReceiptSummary;
|
|
}
|