源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
Dependency audit / audit (push) Canceled after 0s
CI / plan (push) Canceled after 0s
Gitoxide helper admission / macos-latest (push) Canceled after 0s
Gitoxide helper admission / ubuntu-latest (push) Canceled after 0s
Gitoxide helper admission / windows-latest (push) Canceled after 0s
Runtime Host peer admission / quality (push) Canceled after 0s
Windows recovery / windows_recovery (push) Canceled after 0s
CI / heavy (push) Canceled after 0s
CI / test (push) Canceled after 0s
Dependency audit / audit (push) Canceled after 0s
CI / plan (push) Canceled after 0s
Gitoxide helper admission / macos-latest (push) Canceled after 0s
Gitoxide helper admission / ubuntu-latest (push) Canceled after 0s
Gitoxide helper admission / windows-latest (push) Canceled after 0s
Runtime Host peer admission / quality (push) Canceled after 0s
Windows recovery / windows_recovery (push) Canceled after 0s
CI / heavy (push) Canceled after 0s
CI / test (push) Canceled after 0s
This commit is contained in:
commit
7be535059e
3358 files changed
+1306357
No files matched your search
@@ -0,0 +1,895 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { execFile, execFileSync } from 'node:child_process';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { readFile, writeFile } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join, relative, resolve } from 'node:path';
|
||||
|
||||
import { AiSdkBackend } from '../../packages/runtime/dist/ai-sdk-backend.js';
|
||||
import { buildComputerUseTools } from '../../packages/runtime/dist/computer-use-tools.js';
|
||||
import { getAIModel } from '../../packages/runtime/dist/model-factory.js';
|
||||
import { createMakaCuBackend } from '../../packages/computer-use/dist/index.js';
|
||||
import { createDirectRuntimeTurnLedger } from './direct-runtime-ledger.mjs';
|
||||
import { requireComputerUseLabRoot } from './lab-root.mjs';
|
||||
import { sanitizeCuDirectReport } from './report-sanitize.mjs';
|
||||
|
||||
const repoRoot = new URL('../..', import.meta.url).pathname;
|
||||
const binaryPath = join(repoRoot, 'apps/desktop/resources/bin/maka-cu');
|
||||
const labRoot = requireComputerUseLabRoot();
|
||||
const expectedAppPath = join(labRoot, 'test-app/build/Codex CUA Lab.app');
|
||||
const statePath = join(labRoot, 'test-app/runtime/state.json');
|
||||
const fixturePID = Number(process.env.MAKA_CU_AX_MODEL_FIXTURE_PID);
|
||||
const inputAgeProbe = process.env.MAKA_CU_AX_MODEL_INPUT_AGE_PROBE;
|
||||
const temporaryDirectory = process.env.MAKA_CU_AX_MODEL_TEMP_DIR;
|
||||
const reportPath = process.env.MAKA_CU_AX_MODEL_REPORT;
|
||||
const provider = process.env.MAKA_CU_MODEL_PROVIDER ?? 'openai';
|
||||
const baseUrl =
|
||||
process.env.MAKA_CU_MODEL_BASE_URL ??
|
||||
(provider === 'anthropic' ? 'http://127.0.0.1:8537' : 'http://127.0.0.1:8538/v1');
|
||||
const modelId =
|
||||
process.env.MAKA_CU_MODEL_ID ?? (provider === 'anthropic' ? 'claude-sonnet-4-6' : 'gpt-5.6-sol');
|
||||
const apiKey =
|
||||
process.env.MAKA_CU_MODEL_API_KEY ?? (provider === 'anthropic' ? 'coproxy' : 'bridge-managed');
|
||||
const scenario = process.env.MAKA_CU_AX_MODEL_SCENARIO ?? 'set-value';
|
||||
const targetValue = 'model-real-ax';
|
||||
// The digest the manifest pins, rather than a copy of it: a hardcoded digest
|
||||
// here goes stale the first time the executor is rebuilt, and the failure it
|
||||
// produces is "refusing to spawn an unrecognized binary", which reads like a
|
||||
// tampering alarm rather than like a stale constant.
|
||||
const pinnedExecutorSha256 = JSON.parse(
|
||||
await readFile(join(repoRoot, 'apps', 'desktop', 'bundled-tools.json'), 'utf8'),
|
||||
).makaCu.binarySha256;
|
||||
const expectedBinarySha256 = process.env.MAKA_CU_AX_MODEL_EXPECTED_SHA256 ?? pinnedExecutorSha256;
|
||||
const resolvedTemporaryDirectory = resolve(temporaryDirectory ?? '');
|
||||
const relativeTemporaryDirectory = relative(resolve(tmpdir()), resolvedTemporaryDirectory);
|
||||
|
||||
if (
|
||||
!Number.isInteger(fixturePID) ||
|
||||
fixturePID <= 0 ||
|
||||
!inputAgeProbe ||
|
||||
!temporaryDirectory ||
|
||||
!reportPath ||
|
||||
relativeTemporaryDirectory.startsWith('..') ||
|
||||
relativeTemporaryDirectory === ''
|
||||
) {
|
||||
throw new Error('real AX model E2E requires launcher-owned inputs');
|
||||
}
|
||||
|
||||
const startedAt = Date.now();
|
||||
const traces = [];
|
||||
const actions = [];
|
||||
const messages = [];
|
||||
const telemetry = [];
|
||||
const semanticOutcomes = [];
|
||||
let freshAxValue;
|
||||
let injectedIntervention = false;
|
||||
let restartRequested = false;
|
||||
let staleRecoveryObserved = false;
|
||||
let ambiguousRecoveryObserved = false;
|
||||
let semanticStructureChangeRejected = false;
|
||||
let ambiguityFixtureMutated = false;
|
||||
let nextId = 0;
|
||||
let now = Date.now();
|
||||
let totalActionAttempts = 0;
|
||||
const actionAttemptCounts = new Map();
|
||||
const fixtureInstances = new Map();
|
||||
const observationTargets = new Map();
|
||||
const generatedAt = new Date().toISOString();
|
||||
const gitRevision = execFileSync('git', ['rev-parse', 'HEAD'], {
|
||||
cwd: repoRoot,
|
||||
encoding: 'utf8',
|
||||
}).trim();
|
||||
const reportLineage = {
|
||||
runId: randomUUID(),
|
||||
gitRevision,
|
||||
generatedAt,
|
||||
contentLineage: {
|
||||
generator: 'scripts/computer-use/real-ax-harness.mjs',
|
||||
gitRevision,
|
||||
generatedAt,
|
||||
},
|
||||
};
|
||||
|
||||
function registerFixtureInstance(app, windowId) {
|
||||
const windowIds = fixtureInstances.get(app.pid) ?? new Set();
|
||||
windowIds.add(windowId);
|
||||
fixtureInstances.set(app.pid, windowIds);
|
||||
}
|
||||
|
||||
function parseObservation(text) {
|
||||
if (typeof text !== 'string') return undefined;
|
||||
const candidates = [text];
|
||||
for (const markerText of ['Fresh observation:\n', 'Fresh observation: ']) {
|
||||
const marker = text.lastIndexOf(markerText);
|
||||
if (marker >= 0) candidates.push(text.slice(marker + markerText.length));
|
||||
}
|
||||
for (const candidate of candidates.reverse()) {
|
||||
try {
|
||||
const value = JSON.parse(candidate);
|
||||
if (
|
||||
value &&
|
||||
typeof value === 'object' &&
|
||||
typeof value.observation_id === 'string' &&
|
||||
Number.isInteger(value.pid) &&
|
||||
Number.isInteger(value.window_id)
|
||||
) {
|
||||
return value;
|
||||
}
|
||||
} catch {
|
||||
// Try the next model-visible JSON projection.
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function actionBudgetFor(currentScenario) {
|
||||
const mutation =
|
||||
currentScenario === 'ax-click' || currentScenario === 'ambiguity'
|
||||
? 'click_element'
|
||||
: 'set_value';
|
||||
return {
|
||||
total: 8,
|
||||
counts: {
|
||||
list_apps: 2,
|
||||
observe: 4,
|
||||
wait: 2,
|
||||
[mutation]:
|
||||
currentScenario === 'intervention-recovery' || currentScenario === 'restart-recovery'
|
||||
? 2
|
||||
: 1,
|
||||
...(currentScenario === 'ax-multi-step' ? { set_value: 1, click_element: 1 } : {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function qualificationScenarioFor(currentScenario) {
|
||||
return {
|
||||
expectedFailures:
|
||||
currentScenario === 'restart-recovery'
|
||||
? [{ action: 'set_value', error: 'target_missing' }]
|
||||
: currentScenario === 'ambiguity'
|
||||
? [
|
||||
{ action: 'click_element', error: 'stale_frame' },
|
||||
{ action: 'click_element', error: 'target_changed' },
|
||||
]
|
||||
: [],
|
||||
};
|
||||
}
|
||||
|
||||
const qualificationScenario = qualificationScenarioFor(scenario);
|
||||
|
||||
async function physicalInputRecentlyActive() {
|
||||
const output = await new Promise((resolvePromise, reject) => {
|
||||
execFile(
|
||||
inputAgeProbe,
|
||||
[],
|
||||
{
|
||||
encoding: 'utf8',
|
||||
timeout: 1_000,
|
||||
},
|
||||
(error, stdout, stderr) => {
|
||||
if (error) reject(new Error(stderr || error.message));
|
||||
else resolvePromise(stdout.trim());
|
||||
},
|
||||
);
|
||||
});
|
||||
const ageSeconds = Number(output);
|
||||
if (!Number.isFinite(ageSeconds)) {
|
||||
throw new Error(`invalid physical input age: ${output}`);
|
||||
}
|
||||
return ageSeconds < 1;
|
||||
}
|
||||
|
||||
const backend = createMakaCuBackend({
|
||||
binaryPath,
|
||||
expectedBinarySha256,
|
||||
timeoutMs: 10_000,
|
||||
physicalInputRecentlyActive,
|
||||
allowCompatibilityInputDispatch: false,
|
||||
onTrace(event) {
|
||||
traces.push(event);
|
||||
},
|
||||
});
|
||||
|
||||
const appSummaries = await backend.listApps(new AbortController().signal);
|
||||
const fixtureMatches = appSummaries.filter(
|
||||
(app) =>
|
||||
app.pid === fixturePID &&
|
||||
(app.name === 'Codex CUA Lab' ||
|
||||
app.windows?.some((window) => window.title === 'Codex CUA Lab')),
|
||||
);
|
||||
if (fixtureMatches.length !== 1) {
|
||||
throw new Error(`expected one exact fixture app, got ${fixtureMatches.length}`);
|
||||
}
|
||||
let fixture = fixtureMatches[0];
|
||||
const windows = fixture.windows?.filter((window) => window.title === 'Codex CUA Lab') ?? [];
|
||||
if (windows.length !== 1) {
|
||||
throw new Error(`expected one exact fixture window, got ${windows.length}`);
|
||||
}
|
||||
let fixtureWindowId = windows[0].windowId;
|
||||
registerFixtureInstance(fixture, fixtureWindowId);
|
||||
|
||||
const instrumentedBackend = {
|
||||
...backend,
|
||||
async observeApp(input, signal, context) {
|
||||
const observation = await backend.observeApp(input, signal, context);
|
||||
if (
|
||||
scenario === 'ambiguity' &&
|
||||
context.sessionId === 'real-ax-model-e2e' &&
|
||||
!ambiguityFixtureMutated
|
||||
) {
|
||||
ambiguityFixtureMutated = true;
|
||||
const setupContext = {
|
||||
sessionId: 'real-ax-model-ambiguity-setup',
|
||||
turnId: 'setup',
|
||||
toolCallId: 'fixture-mutate-ambiguity',
|
||||
};
|
||||
const setupObservation = await backend.observeApp(
|
||||
{
|
||||
app: fixture.appId,
|
||||
windowId: fixtureWindowId,
|
||||
includeScreenshot: true,
|
||||
},
|
||||
signal,
|
||||
setupContext,
|
||||
);
|
||||
const mutation = setupObservation.elements.find(
|
||||
(element) => element.label === 'CUA Lab Duplicate Stale Target',
|
||||
);
|
||||
if (!mutation) throw new Error('ambiguity mutation control is missing');
|
||||
const setupResult = await backend.runSemantic(
|
||||
{
|
||||
type: 'click_element',
|
||||
observationId: setupObservation.observationId,
|
||||
elementId: mutation.elementId,
|
||||
elementIdentity: mutation.identity,
|
||||
},
|
||||
signal,
|
||||
setupContext,
|
||||
);
|
||||
if (!setupResult.outcome.ok) {
|
||||
throw new Error(`ambiguity fixture mutation failed: ${setupResult.outcome.message}`);
|
||||
}
|
||||
}
|
||||
if (scenario === 'restart-recovery' && !restartRequested) {
|
||||
restartRequested = true;
|
||||
await writeFile(
|
||||
join(resolvedTemporaryDirectory, 'restart-request.json'),
|
||||
`${JSON.stringify({ oldPID: observation.pid })}\n`,
|
||||
{ flag: 'wx', mode: 0o600 },
|
||||
);
|
||||
const completePath = join(resolvedTemporaryDirectory, 'restart-complete.json');
|
||||
const deadline = Date.now() + 15_000;
|
||||
let completed;
|
||||
while (Date.now() < deadline) {
|
||||
try {
|
||||
completed = JSON.parse(await readFile(completePath, 'utf8'));
|
||||
break;
|
||||
} catch (error) {
|
||||
if (error?.code !== 'ENOENT' && !(error instanceof SyntaxError)) throw error;
|
||||
}
|
||||
await new Promise((resolvePromise) => setTimeout(resolvePromise, 50));
|
||||
}
|
||||
if (!completed) throw new Error('restart completion timeout');
|
||||
const apps = await backend.listApps(signal);
|
||||
const currentMatches = apps.filter(
|
||||
(app) =>
|
||||
app.pid === completed.newPID &&
|
||||
(app.name === 'Codex CUA Lab' ||
|
||||
app.windows?.some((window) => window.title === 'Codex CUA Lab')),
|
||||
);
|
||||
if (currentMatches.length !== 1) {
|
||||
throw new Error(`expected one restarted fixture, got ${currentMatches.length}`);
|
||||
}
|
||||
fixture = currentMatches[0];
|
||||
const currentWindows =
|
||||
fixture.windows?.filter((window) => window.title === 'Codex CUA Lab') ?? [];
|
||||
if (currentWindows.length !== 1) {
|
||||
throw new Error(`expected one restarted fixture window, got ${currentWindows.length}`);
|
||||
}
|
||||
fixtureWindowId = currentWindows[0].windowId;
|
||||
registerFixtureInstance(fixture, fixtureWindowId);
|
||||
}
|
||||
return observation;
|
||||
},
|
||||
async runSemantic(action, signal, context) {
|
||||
if (
|
||||
scenario === 'intervention-recovery' &&
|
||||
action.type === 'set_value' &&
|
||||
!injectedIntervention
|
||||
) {
|
||||
injectedIntervention = true;
|
||||
return {
|
||||
outcome: {
|
||||
ok: false,
|
||||
error: 'user_intervened',
|
||||
message: 'synthetic intervention gate requires a fresh observation',
|
||||
},
|
||||
};
|
||||
}
|
||||
const result = await backend.runSemantic(action, signal, context);
|
||||
semanticOutcomes.push({
|
||||
actionType: action.type,
|
||||
ok: result.outcome.ok,
|
||||
...(!result.outcome.ok
|
||||
? {
|
||||
error: result.outcome.error,
|
||||
ambiguous: /ambiguous/i.test(result.outcome.message),
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
if (
|
||||
scenario === 'restart-recovery' &&
|
||||
!result.outcome.ok &&
|
||||
result.outcome.error === 'target_missing'
|
||||
) {
|
||||
staleRecoveryObserved = true;
|
||||
}
|
||||
if (
|
||||
scenario === 'ambiguity' &&
|
||||
action.type === 'click_element' &&
|
||||
!result.outcome.ok &&
|
||||
result.outcome.error === 'stale_frame' &&
|
||||
/ambiguous/.test(result.outcome.message)
|
||||
) {
|
||||
ambiguousRecoveryObserved = true;
|
||||
}
|
||||
if (
|
||||
scenario === 'ambiguity' &&
|
||||
action.type === 'click_element' &&
|
||||
!result.outcome.ok &&
|
||||
result.outcome.error === 'target_changed'
|
||||
) {
|
||||
semanticStructureChangeRejected = true;
|
||||
}
|
||||
if (action.type === 'set_value' && result.outcome.ok) {
|
||||
const field = result.observation?.elements.find(
|
||||
(element) => element.label === 'CUA Lab Set Value Field',
|
||||
);
|
||||
freshAxValue = result.outcome.evidence?.readbackValue ?? field?.value;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
};
|
||||
const [rawComputerTool] = buildComputerUseTools({ backend: instrumentedBackend });
|
||||
const computerTool = {
|
||||
...rawComputerTool,
|
||||
async impl(args, context) {
|
||||
const action = typeof args?.action === 'string' ? args.action : 'unknown';
|
||||
const allowed =
|
||||
scenario === 'observe-only'
|
||||
? new Set(['list_apps', 'observe', 'wait'])
|
||||
: scenario === 'ax-click' || scenario === 'ambiguity'
|
||||
? new Set(['list_apps', 'observe', 'click_element', 'wait'])
|
||||
: scenario === 'ax-multi-step'
|
||||
? new Set(['list_apps', 'observe', 'set_value', 'click_element', 'wait'])
|
||||
: new Set(['list_apps', 'observe', 'set_value', 'wait']);
|
||||
const budget = actionBudgetFor(scenario);
|
||||
const nextTotal = totalActionAttempts + 1;
|
||||
const nextActionCount = (actionAttemptCounts.get(action) ?? 0) + 1;
|
||||
totalActionAttempts = nextTotal;
|
||||
actionAttemptCounts.set(action, nextActionCount);
|
||||
const sourceTarget = observationTargets.get(args?.observation_id);
|
||||
const ownedWindows = fixtureInstances.get(sourceTarget?.pid);
|
||||
const attempt = {
|
||||
action: { type: action },
|
||||
toolCallId: context.toolCallId,
|
||||
sourceObservationId: args?.observation_id,
|
||||
targetPid: sourceTarget?.pid,
|
||||
targetWindowId: sourceTarget?.windowId,
|
||||
targetOwned: Boolean(ownedWindows?.has(sourceTarget?.windowId)),
|
||||
success: false,
|
||||
durationMs: 0,
|
||||
};
|
||||
actions.push(attempt);
|
||||
const rejectAttempt = (resultCode, message) => {
|
||||
attempt.text = `maka_computer.${action} failed: ${resultCode}`;
|
||||
throw new Error(message);
|
||||
};
|
||||
if (!allowed.has(action)) {
|
||||
rejectAttempt(
|
||||
'unsupported_action_policy',
|
||||
`model action '${action}' is outside the AX-only scenario budget`,
|
||||
);
|
||||
}
|
||||
if (nextTotal > budget.total || nextActionCount > (budget.counts[action] ?? 0)) {
|
||||
rejectAttempt('action_budget_exceeded', 'AX-only scenario action budget exceeded');
|
||||
}
|
||||
if (action === 'observe') {
|
||||
// The `computer` tool contract is "observe requires app OR window_id", so a
|
||||
// model that supplies only one of them is behaving correctly. Requiring both
|
||||
// would fail every compliant model before it ever reaches a dispatch. Assert
|
||||
// the real invariant instead: whatever the model did supply must identify the
|
||||
// fixture, and must not point at any other target.
|
||||
const appProvided = args.app !== undefined;
|
||||
const windowProvided = args.window_id !== undefined;
|
||||
const appMatches = args.app === fixture.appId;
|
||||
const windowMatches = args.window_id === fixtureWindowId;
|
||||
const identifiesFixture = (appProvided && appMatches) || (windowProvided && windowMatches);
|
||||
const contradictsFixture = (appProvided && !appMatches) || (windowProvided && !windowMatches);
|
||||
if (!identifiesFixture || contradictsFixture) {
|
||||
rejectAttempt(
|
||||
'target_mismatch',
|
||||
'model observe target does not match the exact fixture identity',
|
||||
);
|
||||
}
|
||||
}
|
||||
const actionStartedAt = Date.now();
|
||||
let result;
|
||||
try {
|
||||
result = await rawComputerTool.impl(args, context);
|
||||
} catch (error) {
|
||||
const resultCode = typeof error?.code === 'string' ? error.code : 'tool_error';
|
||||
attempt.durationMs = Date.now() - actionStartedAt;
|
||||
attempt.text = `maka_computer.${action} failed: ${resultCode}`;
|
||||
throw error;
|
||||
}
|
||||
const observation = parseObservation(result?.modelText) ?? parseObservation(result?.text);
|
||||
const dispatch = traces.findLast(
|
||||
(trace) => trace.type === 'dispatch' && trace.toolCallId === context.toolCallId,
|
||||
);
|
||||
const resultCode =
|
||||
result?.error ??
|
||||
(typeof result?.text === 'string'
|
||||
? result.text.match(/\bfailed:\s*([a-z][a-z0-9_]{1,63})\b/i)?.[1]
|
||||
: undefined);
|
||||
if (observation) {
|
||||
observationTargets.set(observation.observation_id, {
|
||||
pid: observation.pid,
|
||||
windowId: observation.window_id,
|
||||
});
|
||||
}
|
||||
const targetPid = observation?.pid ?? dispatch?.pid ?? sourceTarget?.pid;
|
||||
const targetWindowId = observation?.window_id ?? dispatch?.windowId ?? sourceTarget?.windowId;
|
||||
const resultOwnedWindows = fixtureInstances.get(targetPid);
|
||||
Object.assign(attempt, {
|
||||
resultObservationId: observation?.observation_id,
|
||||
targetPid,
|
||||
targetWindowId,
|
||||
targetOwned: Boolean(resultOwnedWindows?.has(targetWindowId)),
|
||||
success: !resultCode,
|
||||
expectedFailure: qualificationScenario.expectedFailures.some(
|
||||
(expected) => expected.action === action && expected.error === resultCode,
|
||||
),
|
||||
durationMs: Date.now() - actionStartedAt,
|
||||
text:
|
||||
result?.text ?? (resultCode ? `maka_computer.${action} failed: ${resultCode}` : undefined),
|
||||
});
|
||||
return result;
|
||||
},
|
||||
};
|
||||
|
||||
const connection = {
|
||||
slug: provider === 'anthropic' ? 'coproxy' : 'azure-bridge',
|
||||
name: provider === 'anthropic' ? 'Anthropic Coproxy' : 'Azure Bridge',
|
||||
providerType: provider,
|
||||
baseUrl,
|
||||
defaultModel: modelId,
|
||||
enabled: true,
|
||||
createdAt: 1,
|
||||
updatedAt: 1,
|
||||
};
|
||||
const task =
|
||||
scenario === 'observe-only'
|
||||
? 'Use Maka Computer to inspect "Codex CUA Lab". Start with list_apps, observe ' +
|
||||
'the exact app/window, report that "CUA Lab Set Value Field" is visible, ' +
|
||||
'do not mutate anything, then finish.'
|
||||
: scenario === 'ax-click'
|
||||
? 'Use Maka Computer to click the element labeled "CUA Lab Primary Button" exactly ' +
|
||||
'once in "Codex CUA Lab". Start with list_apps and observe, use click_element ' +
|
||||
'with IDs from that observation, verify the fresh state, then finish. Never use coordinates.'
|
||||
: scenario === 'ax-multi-step'
|
||||
? `Use Maka Computer in "Codex CUA Lab" to complete two semantic actions in order. ` +
|
||||
`First set "CUA Lab Set Value Field" to "${targetValue}" with set_value. ` +
|
||||
'Then use the fresh observation returned by that action to click ' +
|
||||
'"CUA Lab Primary Button" exactly once with click_element. Verify both fresh ' +
|
||||
'results, then finish. Never use coordinates, scroll, drag, type, or key actions.'
|
||||
: scenario === 'ambiguity'
|
||||
? 'Use Maka Computer to click the observed element labeled "CUA Lab Stale Target" ' +
|
||||
'exactly once in "Codex CUA Lab". Start with list_apps and observe, then use ' +
|
||||
'click_element with the exact observation_id and element_id. If the tool rejects ' +
|
||||
'the target, report the failure and stop rather than guessing or using coordinates.'
|
||||
: `Use Maka Computer to set "CUA Lab Set Value Field" in "Codex CUA Lab" ` +
|
||||
`to "${targetValue}". Start with list_apps, observe the exact app/window, ` +
|
||||
'use set_value with the observation_id and element_id from that observation, ' +
|
||||
'verify the fresh observation value, then finish. If user_intervened is returned, ' +
|
||||
'observe again before retrying. If target_missing or stale_frame is returned, ' +
|
||||
'list apps and observe the current process again before retrying. Never use ' +
|
||||
'coordinate, click, scroll, drag, type, or key actions.';
|
||||
const turnId = 'turn-real-ax-model';
|
||||
const durableTurn = createDirectRuntimeTurnLedger({
|
||||
sessionId: 'real-ax-model-e2e',
|
||||
turnId,
|
||||
text: task,
|
||||
newId: () => `runtime-event-${++nextId}`,
|
||||
now: () => ++now,
|
||||
});
|
||||
const runtime = new AiSdkBackend({
|
||||
sessionId: 'real-ax-model-e2e',
|
||||
header: {
|
||||
id: 'real-ax-model-e2e',
|
||||
workspaceRoot: repoRoot,
|
||||
cwd: repoRoot,
|
||||
createdAt: now,
|
||||
lastUsedAt: now,
|
||||
name: 'Real AX Model E2E',
|
||||
isFlagged: false,
|
||||
labels: [],
|
||||
isArchived: false,
|
||||
status: 'active',
|
||||
statusUpdatedAt: now,
|
||||
hasUnread: false,
|
||||
backend: 'ai-sdk',
|
||||
llmConnectionSlug: connection.slug,
|
||||
connectionLocked: true,
|
||||
model: modelId,
|
||||
permissionMode: 'bypass',
|
||||
schemaVersion: 1,
|
||||
},
|
||||
appendMessage: async (message) => {
|
||||
messages.push(message);
|
||||
},
|
||||
connection,
|
||||
apiKey,
|
||||
modelId,
|
||||
readExecutionBoundary: async () => ({ kind: 'bypass', revision: 0 }),
|
||||
modelFactory: (input) => getAIModel(input),
|
||||
tools: [computerTool],
|
||||
maxSteps: 8,
|
||||
loadTurnRuntimeEvents: durableTurn.loadTurnRuntimeEvents,
|
||||
newId: () => `id-${++nextId}`,
|
||||
now: () => ++now,
|
||||
recordToolInvocation: (record) => {
|
||||
telemetry.push({
|
||||
toolName: record.toolName,
|
||||
status: record.status,
|
||||
errorClass: record.errorClass,
|
||||
argsSummary: record.argsSummary,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const events = [];
|
||||
let terminalEvent;
|
||||
try {
|
||||
for await (const event of runtime.send({
|
||||
turnId,
|
||||
text: task,
|
||||
context: [],
|
||||
headAnchorRuntimeEvent: durableTurn.anchor,
|
||||
})) {
|
||||
durableTurn.record(event);
|
||||
events.push(event.type);
|
||||
if (event.type === 'complete' || event.type === 'abort' || event.type === 'error') {
|
||||
terminalEvent = {
|
||||
type: event.type,
|
||||
...(typeof event.stopReason === 'string' ? { stopReason: event.stopReason } : {}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const fixtureState = JSON.parse(await readFile(statePath, 'utf8'));
|
||||
const setValueDispatches = traces.filter(
|
||||
(trace) =>
|
||||
trace.type === 'dispatch' && trace.actionType === 'set_value' && trace.address === 'ax',
|
||||
);
|
||||
const clickDispatches = traces.filter(
|
||||
(trace) =>
|
||||
trace.type === 'dispatch' && trace.actionType === 'click_element' && trace.address === 'ax',
|
||||
);
|
||||
const modelClickDispatches = clickDispatches.filter(
|
||||
(trace) => trace.toolCallId !== 'fixture-mutate-ambiguity',
|
||||
);
|
||||
const setupClickDispatches = clickDispatches.filter(
|
||||
(trace) => trace.toolCallId === 'fixture-mutate-ambiguity',
|
||||
);
|
||||
const forbiddenDispatches = traces.filter(
|
||||
(trace) => trace.type === 'dispatch' && trace.address === 'px',
|
||||
);
|
||||
if (
|
||||
!['observe-only', 'ax-click', 'ambiguity'].includes(scenario) &&
|
||||
freshAxValue !== targetValue
|
||||
) {
|
||||
throw new Error(`fresh AX value mismatch: ${String(freshAxValue)}`);
|
||||
}
|
||||
const expectedSetValueDispatches =
|
||||
scenario === 'observe-only' || scenario === 'ax-click' || scenario === 'ambiguity' ? 0 : 1;
|
||||
if (setValueDispatches.length !== expectedSetValueDispatches) {
|
||||
throw new Error(
|
||||
`expected ${expectedSetValueDispatches} AX set_value dispatches, got ${setValueDispatches.length}`,
|
||||
);
|
||||
}
|
||||
const expectedClickDispatches = scenario === 'ax-click' || scenario === 'ax-multi-step' ? 1 : 0;
|
||||
if (modelClickDispatches.length !== expectedClickDispatches) {
|
||||
throw new Error(
|
||||
`expected ${expectedClickDispatches} model AX click dispatches, got ${modelClickDispatches.length}`,
|
||||
);
|
||||
}
|
||||
if (scenario === 'ambiguity' && setupClickDispatches.length !== 1) {
|
||||
throw new Error(`expected one ambiguity setup AX dispatch, got ${setupClickDispatches.length}`);
|
||||
}
|
||||
if (forbiddenDispatches.length !== 0) {
|
||||
throw new Error(`forbidden pixel dispatch count: ${forbiddenDispatches.length}`);
|
||||
}
|
||||
if (scenario === 'restart-recovery' && !staleRecoveryObserved) {
|
||||
throw new Error('model restart scenario did not observe target_missing');
|
||||
}
|
||||
if (scenario === 'restart-recovery') {
|
||||
const staleIndex = actions.findIndex(
|
||||
(record) =>
|
||||
record.action.type === 'set_value' &&
|
||||
record.success === false &&
|
||||
record.expectedFailure === true,
|
||||
);
|
||||
const freshIndex = actions.findIndex(
|
||||
(record, index) =>
|
||||
index > staleIndex &&
|
||||
record.action.type === 'observe' &&
|
||||
record.success === true &&
|
||||
typeof record.resultObservationId === 'string' &&
|
||||
record.targetPid === fixture.pid,
|
||||
);
|
||||
const retry = actions.find(
|
||||
(record, index) =>
|
||||
index > freshIndex &&
|
||||
record.action.type === 'set_value' &&
|
||||
record.success === true &&
|
||||
record.sourceObservationId === actions[freshIndex]?.resultObservationId &&
|
||||
record.targetPid === fixture.pid,
|
||||
);
|
||||
const retryDispatch =
|
||||
retry &&
|
||||
setValueDispatches.some(
|
||||
(trace) =>
|
||||
trace.toolCallId === retry.toolCallId &&
|
||||
trace.pid === retry.targetPid &&
|
||||
trace.windowId === retry.targetWindowId,
|
||||
);
|
||||
if (staleIndex < 0 || freshIndex < 0 || !retry || !retryDispatch) {
|
||||
throw new Error(
|
||||
'restart recovery requires stale target_missing, fresh observation, and successful AX retry',
|
||||
);
|
||||
}
|
||||
}
|
||||
if (
|
||||
scenario === 'ambiguity' &&
|
||||
(!actions.some((record) => record.action.type === 'click_element') ||
|
||||
(!ambiguousRecoveryObserved && !semanticStructureChangeRejected))
|
||||
) {
|
||||
throw new Error(
|
||||
`model ambiguity scenario did not attempt and observe fail-closed rejection: ${JSON.stringify(
|
||||
{
|
||||
actionTypes: actions.map((record) => record.action.type),
|
||||
semanticOutcomes,
|
||||
setupClickDispatches: setupClickDispatches.length,
|
||||
modelClickDispatches: modelClickDispatches.length,
|
||||
},
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
if (
|
||||
(scenario === 'ax-click' || scenario === 'ax-multi-step') &&
|
||||
fixtureState.controls.buttonClickCount !== 1
|
||||
) {
|
||||
throw new Error(`primary button count mismatch: ${fixtureState.controls.buttonClickCount}`);
|
||||
}
|
||||
if (
|
||||
scenario === 'ambiguity' &&
|
||||
(fixtureState.hierarchy.mode !== 'ambiguous' ||
|
||||
fixtureState.hierarchy.staleTargetClickCount !== 0 ||
|
||||
fixtureState.hierarchy.wrongTargetClickCount !== 0)
|
||||
) {
|
||||
throw new Error(`ambiguous stale target mutated: ${JSON.stringify(fixtureState.hierarchy)}`);
|
||||
}
|
||||
if (
|
||||
fixtureState.synthetic !== true ||
|
||||
fixtureState.appPath !== expectedAppPath ||
|
||||
fixtureState.oop?.hostPID !== fixture.pid
|
||||
) {
|
||||
throw new Error('fixture identity changed during model execution');
|
||||
}
|
||||
if (terminalEvent?.type !== 'complete' || terminalEvent.stopReason !== 'end_turn') {
|
||||
throw new Error(`model Runtime did not complete/end_turn: ${JSON.stringify(terminalEvent)}`);
|
||||
}
|
||||
if (scenario === 'ax-multi-step') {
|
||||
const setIndex = actions.findIndex((record) => record.action.type === 'set_value');
|
||||
const clickIndex = actions.findIndex((record) => record.action.type === 'click_element');
|
||||
const setAction = actions[setIndex];
|
||||
const clickAction = actions[clickIndex];
|
||||
if (
|
||||
setIndex < 0 ||
|
||||
clickIndex <= setIndex ||
|
||||
typeof setAction?.resultObservationId !== 'string' ||
|
||||
clickAction?.sourceObservationId !== setAction.resultObservationId
|
||||
) {
|
||||
throw new Error(
|
||||
`multi-step action order or observation lineage mismatch: ${JSON.stringify(
|
||||
actions.map((record) => ({
|
||||
type: record.action.type,
|
||||
sourceObservationId: record.sourceObservationId,
|
||||
resultObservationId: record.resultObservationId,
|
||||
})),
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const evidenceClass = scenario === 'intervention-recovery' ? 'fault-injection' : 'real-runtime';
|
||||
const canonicalFixtureState = {
|
||||
target: {
|
||||
setValue: fixtureState.controls.setValue,
|
||||
buttonClickCount: fixtureState.controls.buttonClickCount,
|
||||
hierarchyMode: fixtureState.hierarchy.mode,
|
||||
staleTargetClickCount: fixtureState.hierarchy.staleTargetClickCount,
|
||||
wrongTargetClickCount: fixtureState.hierarchy.wrongTargetClickCount,
|
||||
},
|
||||
};
|
||||
const fixtureIdentityInstances = [...fixtureInstances.entries()].map(([pid, windowIds]) => ({
|
||||
pid,
|
||||
windowIds: [...windowIds],
|
||||
}));
|
||||
const minimumActionsPassed =
|
||||
scenario !== 'ambiguity' || actions.some((record) => record.action.type === 'click_element');
|
||||
const actionsWithinBudget =
|
||||
totalActionAttempts <= actionBudgetFor(scenario).total &&
|
||||
[...actionAttemptCounts].every(
|
||||
([action, count]) => count <= (actionBudgetFor(scenario).counts[action] ?? 0),
|
||||
);
|
||||
const actionResultsPassed = actions.every(
|
||||
(record) => record.success === true || record.expectedFailure === true,
|
||||
);
|
||||
const dispatchPathPassed =
|
||||
forbiddenDispatches.length === 0 &&
|
||||
setValueDispatches.length === expectedSetValueDispatches &&
|
||||
modelClickDispatches.length === expectedClickDispatches;
|
||||
const qualified =
|
||||
minimumActionsPassed && actionsWithinBudget && actionResultsPassed && dispatchPathPassed;
|
||||
const sanitized = sanitizeCuDirectReport({
|
||||
schemaVersion: 1,
|
||||
...reportLineage,
|
||||
evidenceClass,
|
||||
scenarioId:
|
||||
scenario === 'observe-only'
|
||||
? 'appkit-ax-observe-only'
|
||||
: scenario === 'intervention-recovery'
|
||||
? 'appkit-ax-intervention-recovery'
|
||||
: scenario === 'restart-recovery'
|
||||
? 'appkit-ax-restart-recovery'
|
||||
: scenario === 'ax-click'
|
||||
? 'appkit-ax-click'
|
||||
: scenario === 'ax-multi-step'
|
||||
? 'appkit-ax-multi-step'
|
||||
: scenario === 'ambiguity'
|
||||
? 'appkit-ax-ambiguity'
|
||||
: 'appkit-ax-set-value',
|
||||
policyMode: 'enforced',
|
||||
qualificationEligible: evidenceClass === 'real-runtime',
|
||||
producer: 'computer-use/real-ax',
|
||||
transportClass: 'live-network',
|
||||
model: modelId,
|
||||
provider,
|
||||
baseUrl,
|
||||
status: qualified ? 'pass' : 'fail',
|
||||
terminal: terminalEvent,
|
||||
fixtureIdentity: {
|
||||
instances: fixtureIdentityInstances,
|
||||
},
|
||||
...(evidenceClass === 'fault-injection'
|
||||
? {
|
||||
faultInjection: {
|
||||
layer: 'runtime',
|
||||
kind: 'user_intervened',
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
totalLatencyMs: Date.now() - startedAt,
|
||||
loopStatus: 'completed',
|
||||
state: {
|
||||
fixtureIdentityValid: true,
|
||||
freshAxValueMatched:
|
||||
scenario === 'observe-only' || scenario === 'ax-click' || scenario === 'ambiguity'
|
||||
? undefined
|
||||
: freshAxValue === targetValue,
|
||||
injectedIntervention,
|
||||
staleRecoveryObserved,
|
||||
ambiguousRecoveryObserved,
|
||||
semanticStructureChangeRejected,
|
||||
ambiguityFixtureMutated,
|
||||
setValueDispatches: setValueDispatches.length,
|
||||
clickDispatches: modelClickDispatches.length,
|
||||
setupClickDispatches: setupClickDispatches.length,
|
||||
pixelDispatches: forbiddenDispatches.length,
|
||||
primaryButtonClicks: fixtureState.controls.buttonClickCount,
|
||||
ambiguousClicks: fixtureState.hierarchy.staleTargetClickCount,
|
||||
terminalEvent,
|
||||
toolCallsPersisted: messages.filter((message) => message.type === 'tool_call').length,
|
||||
toolResultsPersisted: messages.filter((message) => message.type === 'tool_result').length,
|
||||
telemetrySuccesses: telemetry.filter((record) => record.status === 'success').length,
|
||||
telemetryErrors: telemetry.filter((record) => record.status === 'error').length,
|
||||
telemetryErrorClasses: [
|
||||
...new Set(
|
||||
telemetry
|
||||
.filter((record) => record.status === 'error')
|
||||
.map((record) => record.errorClass)
|
||||
.filter(Boolean),
|
||||
),
|
||||
],
|
||||
telemetryErrorActions: telemetry
|
||||
.filter((record) => record.status === 'error')
|
||||
.flatMap((record) => {
|
||||
try {
|
||||
const summary = JSON.parse(record.argsSummary ?? '{}');
|
||||
return typeof summary.action === 'string' ? [summary.action] : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}),
|
||||
semanticFailures: semanticOutcomes.filter((outcome) => !outcome.ok).length,
|
||||
semanticFailureCode: semanticOutcomes.find((outcome) => !outcome.ok)?.error,
|
||||
ambiguousSemanticFailures: semanticOutcomes.filter(
|
||||
(outcome) => !outcome.ok && outcome.ambiguous,
|
||||
).length,
|
||||
},
|
||||
actionAttempts: totalActionAttempts,
|
||||
actionCount: actions.length,
|
||||
actionCounts: Object.fromEntries(
|
||||
actions.reduce((counts, record) => {
|
||||
const type = record.action.type;
|
||||
counts.set(type, (counts.get(type) ?? 0) + 1);
|
||||
return counts;
|
||||
}, new Map()),
|
||||
),
|
||||
minimumActionsPassed,
|
||||
actionsWithinBudget,
|
||||
dispatchPathPassed,
|
||||
actions,
|
||||
fixtureState: canonicalFixtureState,
|
||||
expectedState: [
|
||||
{
|
||||
windowId: 'target',
|
||||
path: 'buttonClickCount',
|
||||
pass:
|
||||
!['ax-click', 'ax-multi-step'].includes(scenario) ||
|
||||
fixtureState.controls.buttonClickCount === 1,
|
||||
actual: fixtureState.controls.buttonClickCount,
|
||||
},
|
||||
],
|
||||
forbiddenEffects: {
|
||||
status:
|
||||
forbiddenDispatches.length === 0 && fixtureState.hierarchy.wrongTargetClickCount === 0
|
||||
? 'pass'
|
||||
: 'fail',
|
||||
violations: [],
|
||||
},
|
||||
driverTraces: traces.filter((trace) => trace.toolCallId !== 'fixture-mutate-ambiguity'),
|
||||
});
|
||||
await writeFile(reportPath, `${JSON.stringify(sanitized, null, 2)}\n`, {
|
||||
flag: 'wx',
|
||||
mode: 0o600,
|
||||
});
|
||||
process.stdout.write(`${JSON.stringify(sanitized, null, 2)}\n`);
|
||||
if (!qualified) {
|
||||
throw new Error('real AX model qualification rejected canonical action evidence');
|
||||
}
|
||||
} finally {
|
||||
await runtime.dispose();
|
||||
backend.dispose();
|
||||
}
|
||||
Reference in new issue
Block a user