chore: import upstream snapshot with attribution
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:43:05 +08:00
commit 426e9eeabd
41828 changed files with 9656266 additions and 0 deletions
@@ -0,0 +1,115 @@
#!/usr/bin/env node
/**
* Evidence capture for the real-Chromium MiniWoB++ benchmark lane (#10333).
*
* Drives the SAME `BrowserBenchmarkAdapter` + oracle through a real Chromium
* (puppeteer-core) and screenshots each task's start page and post-oracle
* (solved) page, plus a machine-readable scorecard. Artifacts land under
* `test-results/evidence/10333-browser-real-chromium/`.
*
* Usage (from repo root):
* node plugins/plugin-browser/scripts/capture-miniwob-chromium-evidence.mjs
*/
import { mkdirSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
const here = dirname(fileURLToPath(import.meta.url));
const repoRoot = join(here, "..", "..", "..");
const OUT =
process.env.OUT ||
join(repoRoot, "test-results", "evidence", "10333-browser-real-chromium");
mkdirSync(OUT, { recursive: true });
const { createChromiumBenchmarkEngine } = await import(
"../src/benchmark/chromium-executor.ts"
);
const { BrowserBenchmarkAdapter } = await import("../src/benchmark/adapter.ts");
const { MINIWOB_TASKS } = await import("../src/benchmark/tasks.ts");
const { OraclePolicy } = await import("../src/benchmark/policy.ts");
const SEED = Number(process.env.SEED ?? 0);
const engine = await createChromiumBenchmarkEngine({ headless: true });
console.log(`[evidence] engine: ${engine.executablePath}`);
const policy = new OraclePolicy();
const scorecard = [];
let i = 0;
for (const task of MINIWOB_TASKS) {
i += 1;
const { executor, dispose } = await engine.makeExecutor();
const page = engine.currentPage();
const adapter = new BrowserBenchmarkAdapter(executor, {
maxTrajectoryLength: task.maxSteps,
timestampSource: () => 0,
});
const tag = `${String(i).padStart(2, "0")}-${task.id}`;
try {
await adapter.loadTask(task, SEED);
if (page) await page.screenshot({ path: join(OUT, `${tag}-start.png`) });
let steps = 0;
for (let s = 0; s < task.maxSteps && !adapter.isTerminated(); s++) {
const observation = await adapter.getObservation();
const action = await policy.act({
observation,
task,
seed: SEED,
history: adapter.getTrajectory(),
});
const r = await adapter.step(action);
steps++;
if (r.done) break;
}
const reward = await task.reward(adapter.rewardContext(), SEED);
if (page) await page.screenshot({ path: join(OUT, `${tag}-solved.png`) });
const trajectory = adapter.getTrajectory().map((st) => ({
action: st.action,
resultMode: st.commandResult?.mode ?? null,
error: st.error ? `${st.error.code}: ${st.error.message}` : null,
}));
scorecard.push({
task: task.id,
seed: SEED,
engine: executor.engine,
utterance: task.utterance(SEED),
reward,
success: reward >= 1,
steps,
trajectory,
});
console.log(
`[evidence] ${tag}: reward=${reward} (${reward >= 1 ? "SOLVED" : "FAILED"}) — ${steps} steps`,
);
} catch (err) {
scorecard.push({ task: task.id, seed: SEED, error: String(err) });
console.log(`[evidence] ${tag}: ERROR ${err}`);
} finally {
await dispose();
}
}
await engine.close();
const solved = scorecard.filter((s) => s.success).length;
const report = {
benchmark: "miniwob++",
engine: "chromium",
executablePath: engine.executablePath,
policy: "oracle",
seed: SEED,
total: scorecard.length,
solved,
successRate: scorecard.length ? solved / scorecard.length : 0,
episodes: scorecard,
};
writeFileSync(
join(OUT, "scorecard.json"),
`${JSON.stringify(report, null, 2)}\n`,
);
console.log(
`\n[evidence] ${solved}/${scorecard.length} solved on real Chromium → ${OUT}`,
);
process.exit(solved === scorecard.length ? 0 : 1);
@@ -0,0 +1,105 @@
#!/usr/bin/env node
/**
* Screen-recording evidence for the real-Chromium MiniWoB++ lane (#10333).
*
* Drives the multi-page `multistep-purchase` oracle (home -> catalog -> buy)
* through a real Chromium step by step, screenshots each step, and stitches the
* frames into an animated GIF (via ffmpeg) so a reviewer can watch the agent
* navigate a real browser end to end. Artifacts land under
* `test-results/evidence/10333-browser-real-chromium/`.
*
* Usage (from repo root): bun plugins/plugin-browser/scripts/capture-miniwob-chromium-recording.mjs
*/
import { spawnSync } from "node:child_process";
import { existsSync, mkdirSync, rmSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
const here = dirname(fileURLToPath(import.meta.url));
const repoRoot = join(here, "..", "..", "..");
const OUT = join(
repoRoot,
"test-results",
"evidence",
"10333-browser-real-chromium",
);
mkdirSync(OUT, { recursive: true });
const framesDir = join(OUT, "_frames");
rmSync(framesDir, { recursive: true, force: true });
mkdirSync(framesDir, { recursive: true });
const { createChromiumBenchmarkEngine } = await import(
"../src/benchmark/chromium-executor.ts"
);
const { BrowserBenchmarkAdapter } = await import("../src/benchmark/adapter.ts");
const { getTaskById } = await import("../src/benchmark/tasks.ts");
const { OraclePolicy } = await import("../src/benchmark/policy.ts");
const SEED = 0;
const task = getTaskById("multistep-purchase");
const engine = await createChromiumBenchmarkEngine({ headless: true });
console.log(`[recording] engine: ${engine.executablePath}`);
const { executor, dispose } = await engine.makeExecutor();
const page = engine.currentPage();
const adapter = new BrowserBenchmarkAdapter(executor, {
maxTrajectoryLength: task.maxSteps,
timestampSource: () => 0,
});
const policy = new OraclePolicy();
let frame = 0;
async function snap(label) {
if (!page) return;
const f = String(frame).padStart(3, "0");
await page.screenshot({ path: join(framesDir, `frame-${f}.png`) });
// hold each meaningful frame for ~1s of GIF (3 duplicate frames @ ~3fps feel)
frame += 1;
console.log(`[recording] frame ${f}: ${label}`);
}
await adapter.loadTask(task, SEED);
await snap("start: shop home");
for (let s = 0; s < task.maxSteps && !adapter.isTerminated(); s++) {
const observation = await adapter.getObservation();
const action = await policy.act({
observation,
task,
seed: SEED,
history: adapter.getTrajectory(),
});
await adapter.step(action);
await snap(
`after: ${action.type} ${action.selector ?? ""} (${action.note ?? ""})`,
);
}
const reward = await task.reward(adapter.rewardContext(), SEED);
console.log(
`[recording] reward=${reward} (${reward >= 1 ? "SOLVED" : "FAILED"})`,
);
await dispose();
await engine.close();
// Stitch frames -> GIF (each frame held ~1.1s). Skip cleanly if ffmpeg absent.
const gifPath = join(OUT, "multistep-purchase-walkthrough.gif");
const ff = spawnSync(
"ffmpeg",
[
"-y",
"-framerate",
"0.9",
"-i",
join(framesDir, "frame-%03d.png"),
"-vf",
"scale=800:-1:flags=lanczos",
gifPath,
],
{ stdio: "inherit" },
);
if (ff.status === 0 && existsSync(gifPath)) {
console.log(`[recording] GIF -> ${gifPath}`);
rmSync(framesDir, { recursive: true, force: true });
} else {
console.log("[recording] ffmpeg unavailable — frames kept in _frames/");
}
process.exit(reward >= 1 ? 0 : 1);
@@ -0,0 +1,109 @@
#!/usr/bin/env node
/**
* Evidence capture for the web-element grounding lane (#10333).
*
* Renders each ScreenSpot-Web-style task in a real Chromium, overlays the
* target's TRUE on-screen bbox (orange outline) and the grounder's predicted
* click point (cyan dot), screenshots it, and writes a scorecard. Artifacts land
* under `test-results/evidence/10333-web-grounding/`.
*
* Usage (from repo root): bun plugins/plugin-browser/scripts/capture-web-grounding-evidence.mjs
*/
import { mkdirSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
const here = dirname(fileURLToPath(import.meta.url));
const repoRoot = join(here, "..", "..", "..");
const OUT =
process.env.OUT ||
join(repoRoot, "test-results", "evidence", "10333-web-grounding");
mkdirSync(OUT, { recursive: true });
const { createChromiumBenchmarkEngine } = await import(
"../src/benchmark/chromium-executor.ts"
);
const { scoreWebGrounding, centerGrounder, WEB_GROUNDING_TASKS } = await import(
"../src/benchmark/web-grounding.ts"
);
const engine = await createChromiumBenchmarkEngine({ headless: true });
console.log(`[grounding] engine: ${engine.executablePath}`);
let i = 0;
const captured = [];
const score = await scoreWebGrounding(
engine,
WEB_GROUNDING_TASKS,
centerGrounder,
async ({ task, box, predicted, correct }) => {
i += 1;
const page = engine.currentPage();
if (!page) return;
const tag = `${String(i).padStart(2, "0")}-${task.id}`;
console.log(`[grounding] ${tag}: overlay`);
// Overlay the ground-truth bbox + the predicted point, then screenshot.
await page.evaluate(
({ box, predicted }) => {
const mk = (style) => {
const d = document.createElement("div");
d.style.cssText = style;
d.style.position = "fixed";
d.style.zIndex = "999999";
d.style.pointerEvents = "none";
document.body.appendChild(d);
};
mk(
`left:${box.x}px;top:${box.y}px;width:${box.width}px;height:${box.height}px;` +
`border:3px solid #ff5800;border-radius:6px;box-shadow:0 0 0 2px rgba(255,88,0,.3)`,
);
if (predicted) {
mk(
`left:${predicted.x - 7}px;top:${predicted.y - 7}px;width:14px;height:14px;` +
`background:#34d6ff;border:2px solid #001018;border-radius:50%`,
);
}
},
{ box, predicted },
);
console.log(`[grounding] ${tag}: screenshot`);
// Puppeteer's screenshot path can stall when request interception is still
// enabled on some Chromium builds. The page is already fully rendered and
// overlaid, and this executor page is disposed immediately after the sample.
await page.setRequestInterception(false).catch(() => {});
await page.screenshot({ path: join(OUT, `${tag}.png`) });
captured.push({
tag,
task: task.id,
group: task.group,
correct,
box,
predicted,
});
console.log(
`[grounding] ${tag}: ${correct ? "HIT" : "MISS"} — point (${Math.round(predicted.x)},${Math.round(predicted.y)}) in bbox [${Math.round(box.x)},${Math.round(box.y)},${Math.round(box.width)},${Math.round(box.height)}]`,
);
},
);
await engine.close();
const report = {
benchmark: "web-element-grounding (ScreenSpot-Web style)",
engine: "chromium",
executablePath: engine.executablePath,
grounder: "center",
total: score.total,
correct: score.correct,
accuracy: score.accuracy,
byGroup: score.byGroup,
samples: captured,
};
writeFileSync(
join(OUT, "scorecard.json"),
`${JSON.stringify(report, null, 2)}\n`,
);
console.log(
`\n[grounding] accuracy ${score.correct}/${score.total} (${score.accuracy}) → ${OUT}`,
);
process.exit(score.accuracy === 1 ? 0 : 1);
@@ -0,0 +1,81 @@
#!/usr/bin/env bun
/**
* External browser dataset benchmark runner (#10333).
*
* Runs committed Mind2Web/WebArena-style fixtures through the real
* plugin-browser BROWSER command router and writes an evidence artifact.
*/
import { mkdir, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import {
EXTERNAL_WEB_DATASET_TASKS,
NoopPolicy,
OraclePolicy,
runBenchmarkSuite,
WrongPolicy,
} from "../src/benchmark/index.ts";
const here = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(here, "../../..");
function parseArgs(argv) {
const opts = { policy: "oracle", out: null };
for (let i = 0; i < argv.length; i++) {
const arg = argv[i];
if (arg === "--policy") opts.policy = argv[++i];
else if (arg === "--out") opts.out = argv[++i];
}
return opts;
}
function makePolicy(name) {
if (name === "noop") return new NoopPolicy();
if (name === "wrong") return new WrongPolicy();
return new OraclePolicy();
}
async function main() {
const opts = parseArgs(process.argv.slice(2));
const outPath =
opts.out ??
path.join(
repoRoot,
"test-results/evidence/10333-browser-external-dataset",
`external-dataset-${opts.policy}-run.json`,
);
const report = await runBenchmarkSuite({
benchmarkName: "external-web-dataset",
tasks: EXTERNAL_WEB_DATASET_TASKS,
seeds: [0],
policy: makePolicy(opts.policy),
});
const stamped = { generatedAt: new Date().toISOString(), ...report };
await mkdir(path.dirname(outPath), { recursive: true });
await writeFile(outPath, `${JSON.stringify(stamped, null, 2)}\n`);
console.log(
`\nExternal dataset benchmark - engine=${report.engine} policy=${report.policy}`,
);
console.log("-".repeat(64));
for (const task of report.summary.byTask) {
const mark = task.solved === task.total ? "PASS" : "FAIL";
console.log(
` ${mark} ${task.taskId.padEnd(28)} ${task.solved}/${task.total}`,
);
}
console.log("-".repeat(64));
console.log(
` TOTAL solved ${report.summary.solved}/${report.summary.total} ` +
`(success rate ${(report.summary.successRate * 100).toFixed(1)}%)`,
);
console.log(`\n artifact -> ${path.relative(repoRoot, outPath)}\n`);
}
main().catch((error) => {
console.error(error);
process.exit(1);
});
@@ -0,0 +1,110 @@
#!/usr/bin/env bun
/**
* External browser dataset benchmark runner - REAL Chromium engine (#10333).
*
* Runs the committed Mind2Web/WebArena-style external dataset records through
* the real Chromium benchmark executor and writes an evidence artifact.
* This complements `bench:external`, which keeps the JSDOM web-mode lane fast
* for regular package tests.
*/
import { mkdir, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import {
createChromiumBenchmarkExecutor,
EXTERNAL_WEB_DATASET_TASKS,
launchChromiumBenchmarkBrowser,
NoopPolicy,
OraclePolicy,
resolveChromiumExecutablePath,
runBenchmarkSuite,
WrongPolicy,
} from "../src/benchmark/index.ts";
const here = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(here, "../../..");
function parseArgs(argv) {
const opts = { policy: "oracle", out: null };
for (let i = 0; i < argv.length; i++) {
const arg = argv[i];
if (arg === "--policy") opts.policy = argv[++i];
else if (arg === "--out") opts.out = argv[++i];
}
return opts;
}
function makePolicy(name) {
if (name === "noop") return new NoopPolicy();
if (name === "wrong") return new WrongPolicy();
return new OraclePolicy();
}
async function main() {
const chromium = resolveChromiumExecutablePath();
if (!chromium) {
console.log(
"[external-chromium] no Chromium binary found - skipping " +
"(run `bunx playwright install --with-deps chromium` first).",
);
return;
}
console.log(`[external-chromium] using Chromium at ${chromium}`);
const opts = parseArgs(process.argv.slice(2));
const outPath =
opts.out ??
path.join(
repoRoot,
"test-results/evidence/10333-browser-external-dataset",
`external-dataset-chromium-${opts.policy}-run.json`,
);
const { browser, close } = await launchChromiumBenchmarkBrowser();
let report;
try {
report = await runBenchmarkSuite({
benchmarkName: "external-web-dataset",
tasks: EXTERNAL_WEB_DATASET_TASKS,
seeds: [0],
policy: makePolicy(opts.policy),
makeExecutor: () => createChromiumBenchmarkExecutor({ browser }),
});
} finally {
await close();
}
const stamped = { generatedAt: new Date().toISOString(), ...report };
await mkdir(path.dirname(outPath), { recursive: true });
await writeFile(outPath, `${JSON.stringify(stamped, null, 2)}\n`);
console.log(
`\nExternal dataset benchmark - engine=${report.engine} policy=${report.policy}`,
);
console.log("-".repeat(64));
for (const task of report.summary.byTask) {
const mark = task.solved === task.total ? "PASS" : "FAIL";
console.log(
` ${mark} ${task.taskId.padEnd(28)} ${task.solved}/${task.total}`,
);
}
console.log("-".repeat(64));
console.log(
` TOTAL solved ${report.summary.solved}/${report.summary.total} ` +
`(success rate ${(report.summary.successRate * 100).toFixed(1)}%)`,
);
console.log(`\n artifact -> ${path.relative(repoRoot, outPath)}\n`);
if (
opts.policy === "oracle" &&
report.summary.solved !== report.summary.total
) {
process.exitCode = 1;
}
}
main().catch((error) => {
console.error(error);
process.exit(1);
});
@@ -0,0 +1,102 @@
#!/usr/bin/env bun
/**
* MiniWoB++ browser benchmark runner (#9476).
*
* Runs the MiniWoB++ task suite through the REAL plugin-browser command router
* (`executeBrowserWorkspaceCommand`, web mode) and writes a run-report artifact.
* This is the plugin-browser analog of plugin-computeruse's OSWorld benchmark
* runner — proof that a web-interaction benchmark is wired end-to-end through
* plugin-browser BROWSER actions, not bypassing it via the inference layer.
*
* Usage:
* bun scripts/run-miniwob-benchmark.mjs [--policy oracle|noop|wrong]
* [--seeds N] [--out <file>]
*/
import { mkdir, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import {
NoopPolicy,
OraclePolicy,
runBenchmarkSuite,
WrongPolicy,
} from "../src/benchmark/index.ts";
const here = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(here, "../../..");
function parseArgs(argv) {
const opts = { policy: "oracle", seeds: 3, out: null };
for (let i = 0; i < argv.length; i++) {
const a = argv[i];
if (a === "--policy") opts.policy = argv[++i];
else if (a === "--seeds") opts.seeds = Number(argv[++i]);
else if (a === "--out") opts.out = argv[++i];
}
return opts;
}
function makePolicy(name) {
if (name === "noop") return new NoopPolicy();
if (name === "wrong") return new WrongPolicy();
return new OraclePolicy();
}
async function main() {
const opts = parseArgs(process.argv.slice(2));
const seeds = Array.from({ length: Math.max(1, opts.seeds) }, (_, i) => i);
const outPath =
opts.out ??
path.join(
repoRoot,
"test-results/evidence/9476-browser-benchmark",
`miniwob-${opts.policy}-run.json`,
);
const report = await runBenchmarkSuite({
seeds,
policy: makePolicy(opts.policy),
});
// Stamp the artifact after the deterministic run (clock is not part of the run).
const stamped = { generatedAt: new Date().toISOString(), ...report };
await mkdir(path.dirname(outPath), { recursive: true });
await writeFile(outPath, `${JSON.stringify(stamped, null, 2)}\n`);
// Human-readable summary.
console.log(
`\nMiniWoB++ benchmark — engine=${report.engine} policy=${report.policy}`,
);
console.log("─".repeat(64));
for (const t of report.summary.byTask) {
const bar = t.solved === t.total ? "✓" : "✗";
console.log(` ${bar} ${t.taskId.padEnd(24)} ${t.solved}/${t.total}`);
}
console.log("─".repeat(64));
console.log(
` TOTAL solved ${report.summary.solved}/${report.summary.total} ` +
`(success rate ${(report.summary.successRate * 100).toFixed(1)}%)`,
);
const failures = report.episodes.filter((e) => !e.success);
if (failures.length > 0) {
console.log("\n failing episodes:");
for (const f of failures) {
const steps = f.trajectory
.map((s) => `${s.action.type}${s.error ? `!${s.error}` : ""}`)
.join(" → ");
console.log(
` - ${f.taskId}#${f.seed} reward=${f.reward} steps=[${steps}]` +
`${f.error ? ` err=${f.error}` : ""}`,
);
}
}
console.log(`\n artifact → ${path.relative(repoRoot, outPath)}\n`);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
@@ -0,0 +1,135 @@
#!/usr/bin/env bun
/**
* MiniWoB++ browser benchmark runner — REAL Chromium engine (#10333).
*
* Runs the same MiniWoB++ task suite as `run-miniwob-benchmark.mjs`, but every
* action is dispatched through a real Chromium process
* (`createChromiumBenchmarkExecutor`, puppeteer-core) instead of JSDOM web mode,
* and writes a run-report artifact. This is the deferred "Needs CI infra"
* counterpart to the web-mode lane: proof that the benchmark runs end-to-end
* through plugin-browser BROWSER commands on a real browser engine.
*
* Requires a Chromium binary (run `bunx playwright install --with-deps chromium`,
* or set PUPPETEER_EXECUTABLE_PATH / CHROME_PATH). Skips gracefully (exit 0) when
* none is available so the un-gated path is a no-op.
*
* Usage:
* bun scripts/run-miniwob-chromium-benchmark.mjs [--policy oracle|noop|wrong]
* [--seeds N] [--out <file>]
*/
import { mkdir, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import {
createChromiumBenchmarkExecutor,
launchChromiumBenchmarkBrowser,
NoopPolicy,
OraclePolicy,
resolveChromiumExecutablePath,
runBenchmarkSuite,
WrongPolicy,
} from "../src/benchmark/index.ts";
const here = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(here, "../../..");
function parseArgs(argv) {
const opts = { policy: "oracle", seeds: 3, out: null };
for (let i = 0; i < argv.length; i++) {
const a = argv[i];
if (a === "--policy") opts.policy = argv[++i];
else if (a === "--seeds") opts.seeds = Number(argv[++i]);
else if (a === "--out") opts.out = argv[++i];
}
return opts;
}
function makePolicy(name) {
if (name === "noop") return new NoopPolicy();
if (name === "wrong") return new WrongPolicy();
return new OraclePolicy();
}
async function main() {
const chromium = resolveChromiumExecutablePath();
if (!chromium) {
console.log(
"[miniwob-chromium] no Chromium binary found — skipping " +
"(run `bunx playwright install --with-deps chromium` first).",
);
return;
}
console.log(`[miniwob-chromium] using Chromium at ${chromium}`);
const opts = parseArgs(process.argv.slice(2));
const seeds = Array.from({ length: Math.max(1, opts.seeds) }, (_, i) => i);
const outPath =
opts.out ??
path.join(
repoRoot,
"test-results/evidence/10333-browser-real-chromium",
`miniwob-chromium-${opts.policy}-run.json`,
);
// One browser for the whole suite — a fresh page per episode is cheap.
const { browser, close } = await launchChromiumBenchmarkBrowser();
let report;
try {
report = await runBenchmarkSuite({
seeds,
policy: makePolicy(opts.policy),
makeExecutor: () => createChromiumBenchmarkExecutor({ browser }),
});
} finally {
await close();
}
// Stamp the artifact after the deterministic run (clock is not part of the run).
const stamped = { generatedAt: new Date().toISOString(), ...report };
await mkdir(path.dirname(outPath), { recursive: true });
await writeFile(outPath, `${JSON.stringify(stamped, null, 2)}\n`);
console.log(
`\nMiniWoB++ benchmark — engine=${report.engine} policy=${report.policy}`,
);
console.log("─".repeat(64));
for (const t of report.summary.byTask) {
const bar = t.solved === t.total ? "✓" : "✗";
console.log(` ${bar} ${t.taskId.padEnd(24)} ${t.solved}/${t.total}`);
}
console.log("─".repeat(64));
console.log(
` TOTAL solved ${report.summary.solved}/${report.summary.total} ` +
`(success rate ${(report.summary.successRate * 100).toFixed(1)}%)`,
);
const failures = report.episodes.filter((e) => !e.success);
if (failures.length > 0) {
console.log("\n failing episodes:");
for (const f of failures) {
const steps = f.trajectory
.map((s) => `${s.action.type}${s.error ? `!${s.error}` : ""}`)
.join(" → ");
console.log(
` - ${f.taskId}#${f.seed} reward=${f.reward} steps=[${steps}]` +
`${f.error ? ` err=${f.error}` : ""}`,
);
}
}
console.log(`\n artifact → ${path.relative(repoRoot, outPath)}\n`);
// Non-zero exit on an unexpected oracle failure so CI catches regressions.
if (
opts.policy === "oracle" &&
report.summary.solved !== report.summary.total
) {
process.exitCode = 1;
}
}
main().catch((err) => {
console.error(err);
process.exit(1);
});