Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
5ba6f38
fix(webfetch): unify source validation and bound browser capture
lanyue-llk Oct 20, 2026
8ec939a
fix(webfetch): bind live rules and clean up capture descendants
lanyue-llk Oct 26, 2026
4ff6fdb
fix(webfetch): retain timeout bounds when process discovery fails
lanyue-llk Oct 26, 2026
8a065e5
fix(webfetch): preserve capture isolation when process tools fail
lanyue-llk Oct 26, 2026
1ecd137
fix(webfetch): verify process identity before timeout escalation
lanyue-llk Oct 26, 2026
be558d4
fix(webfetch): ignore stale browser launch identities during cleanup
lanyue-llk Oct 26, 2026
931acdf
fix(webfetch): enforce capture hard deadlines in one termination pass
lanyue-llk Oct 26, 2026
026e0e0
fix(webfetch): retain browser ownership after driver exit
lanyue-llk Oct 26, 2026
412a358
fix(webfetch): clean up browsers launched during timeout handling
lanyue-llk Oct 26, 2026
211486c
fix(webfetch): checkpoint worker communication failures
lanyue-llk Oct 26, 2026
33c8732
fix(webfetch): verify browser identity when process discovery fails
lanyue-llk Oct 26, 2026
d37ade5
fix(webfetch): terminate owned browser process groups
lanyue-llk Oct 26, 2026
e9f89e2
fix(webfetch): preserve legacy evidence and complete mixed captures
lanyue-llk Oct 26, 2026
c90e29f
fix(webfetch): require original rules for legacy certificates
lanyue-llk Oct 26, 2026
f66336a
fix(webfetch): share text qualification across live task kinds
lanyue-llk Oct 26, 2026
96dd15d
fix(webfetch): retain recorded fields in unversioned scopes
lanyue-llk Oct 26, 2026
abaf200
fix(webfetch): verify legacy observation rule authority
lanyue-llk Oct 26, 2026
bcd5b18
refactor: standardize imports and preserve runtime loading
lanyue-llk Oct 26, 2026
c5ee0a8
fix(webfetch): preserve legacy rule authority and standalone worker s…
lanyue-llk Oct 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ When comparing builds of one implementation, do not infer a code-induced latency

Prefer invariant contracts for changing pages: stable labels, required fields, record shape, types, count bounds, uniqueness, and independently observed explanatory text. Do not freeze ranks, prices, member counts, or current titles unless the benchmark is explicitly a time-bound snapshot.

Ground truth must be independent of the provider under test. Retain source location, bytes or a durable observation, hash, capture method, and scope. Partial annotations support only the requirements they cover. They are not a full-body reference.
Ground truth must be independent of the provider under test. Retain source location, bytes or a durable observation, hash, capture method, and scope. Partial annotations support only the requirements they cover. They are not a full-body reference. When authoring or expanding a task, inspect its actual reference text against the reader's main content needs. A dashboard's labels do not establish its values; a feed's navigation does not establish its entries. Select applicable fact, body-region, record, pattern, resource and file checks through the shared scorer. Give the source-complete answer and a page shell or incomplete answer opposite outcomes before freezing the new version. Preserve earlier input versions.

## Machine-readable current profile

Expand Down
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ Keep original-language task text, source quotations and multilingual test fixtur
of shared renderers: read them from the selected profile and bound evidence.
Published historical reports retain their own content and provenance.

Use absolute Python imports and keep ordinary imports at the top of the module.
Keep an import local when loading an optional dependency, preparing a runtime
module, avoiding a circular import or preserving test isolation.

Test both accepted and rejected scenarios. Fix scoring rules rather than editing saved verdicts. Refactoring must preserve historical measurements and certificates.

Write documentation in English. Keep the [Chinese README](docs/README.zh-CN.md) aligned when setup or usage changes; other docs do not need duplicate translations. Preserve original task text, quotations and frozen acceptance inputs. Reports follow their track's language settings.
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ naming-check: runtime-check

.PHONY: lint-check
lint-check: runtime-check
$(TEST_PY) -m ruff check --config 'include=["*.py"]' --select F .
$(TEST_PY) -m ruff check --config 'include=["*.py"]' --select F,TID252 .
$(TEST_PY) -m ruff check

test-python: runtime-check
Expand Down
2 changes: 1 addition & 1 deletion harness/browser_use/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Autonomous browser-use evaluation built on WebArena-Verified."""

from .selection import PILOT_SCHEMA, PilotTask, load_pilot
from harness.browser_use.selection import PILOT_SCHEMA, PilotTask, load_pilot

__all__ = ["PILOT_SCHEMA", "PilotTask", "load_pilot"]
6 changes: 4 additions & 2 deletions harness/browser_use/failure_cause_review.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
from pathlib import Path
from typing import Any

from lexbench_webfetch.paths import contained_path

from harness.web_fetch.safety import unlinked_path

LAYERED_REVIEW_SCHEMA = "lex_browser_use_layered_failure_cause_review/2"
DEFAULT_REVIEW_PATH = (
Path(__file__).parents[2] / "research/browser-use-failure-cause-review.json"
Expand All @@ -19,9 +23,7 @@ def sha256(path: Path) -> str | None:

def review_path(profile: dict[str, Any], *, authority_root: Path | None = None) -> Path:
"""Resolve an explicitly bound external review or a repository-relative review."""
from lexbench_webfetch.paths import contained_path

from harness.web_fetch.safety import unlinked_path
value = (profile.get("failure_cause_review") or {}).get("source_path")
if value is None:
value = "research/browser-use-failure-cause-review.json"
Expand Down
48 changes: 28 additions & 20 deletions harness/browser_use/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,20 @@
from pathlib import Path, PurePosixPath
from typing import Any

from harness.record_io import load_jsonl
from harness.report_languages import write_language_contract
from harness.reporting.assets import bound_json_asset
from harness.reporting.templates import template_identity
from harness.web_fetch.report_style import STYLE as SHARED_STYLE

from .failure_cause_review import (
from harness.browser_use.failure_cause_review import (
LAYERED_REVIEW_SCHEMA,
extract_failure_cause_review_cohort,
load_failure_cause_review,
localized_cause,
validate_failure_cause_review,
)
from .failure_cause_review import (
from harness.browser_use.failure_cause_review import (
review_path as failure_review_path,
)
from .failure_cause_review import (
from harness.browser_use.failure_cause_review import (
sha256 as file_sha256,
)
from .publication import (
from harness.browser_use.publication import (
evaluator_summary,
evidence_metadata,
read_public_json,
Expand All @@ -47,7 +41,23 @@
trajectory,
trajectory_label,
)
from .selection import PilotSelection, load_pilot, report_selection_path, validate_against_official
from harness.browser_use.selection import (
PilotSelection,
load_pilot,
report_selection_path,
validate_against_official,
)
from harness.record_io import load_jsonl
from harness.report_languages import (
add_language_argument,
includes_english,
primary_report_language,
select_report_language,
write_language_contract,
)
from harness.reporting.assets import bound_json_asset
from harness.reporting.templates import template_identity
from harness.web_fetch.report_style import STYLE as SHARED_STYLE

ENGINE_LABELS = {"chromium": "Chromium", "moli": "Moli"}
MODEL_LABELS = {"gpt-5.6-sol": "GPT-5.6 Sol"}
Expand All @@ -62,7 +72,6 @@
RESEARCH_ROOT = Path(__file__).parents[2] / "research"



def _finding_text(diagnosis: dict[str, Any], field: str) -> str:
value = diagnosis.get(field, {})
return str(value.get("zh") or value.get("en") or "") if isinstance(value, dict) else str(value)
Expand Down Expand Up @@ -2256,7 +2265,9 @@ def _trajectory_replay_control(
"""Explain the selected replay comparison without inheriting a past article."""
if bridge is None:
return ""
from .report_i18n import bilingual_text

# Report assets are loaded only when rendering, not for CLI help.
from harness.browser_use.report_i18n import bilingual_text

def text(zh, en):
return Safe(bilingual_text(zh, en))
Expand Down Expand Up @@ -2338,7 +2349,7 @@ def _article_body(
automatic_failure_counts: Counter[str],
trajectory_replay_bridge: dict[str, Any] | None,
) -> str:
from .report_i18n import bilingual_text
from harness.browser_use.report_i18n import bilingual_text

text = bilingual_text
task_count = len(tasks)
Expand Down Expand Up @@ -2750,7 +2761,6 @@ def _load_fix_log(profile: dict[str, Any]) -> dict[str, Any]:
return load_fix_log(profile, sanitize_value=sanitize)



def _write_fix_log(stage: Path, model: dict[str, Any], *, english: bool = False) -> None:
"""Render both views from one manifest; screenshots never imply pixel change."""
lang = "en" if english else "zh"
Expand All @@ -2776,7 +2786,7 @@ def link(identifier: str, label: str) -> str:
def save(name: str, title: str, body: str) -> None:
page = _page(title=title, active="fixes", body=body, depth=1)
if english:
from .report_i18n import localize_html
from harness.browser_use.report_i18n import localize_html

page = localize_html(page, PurePosixPath(f"fixes/{name}"))
(folder / name).write_text(page, encoding="utf-8")
Expand Down Expand Up @@ -2847,9 +2857,8 @@ def save(name: str, title: str, body: str) -> None:


def build_report(*, evidence: Path | Iterable[Path], output: Path, selection_path: Path, include_english: bool = False, language: str | None = None, profile_path: Path | None = None) -> Path:
from harness.report_languages import includes_english, select_report_language
from harness.browser_use.report_i18n import bilingual_text

from .report_i18n import bilingual_text
include_english = includes_english(language, include_english)
if profile_path is None:
raise ValueError("An explicit report profile is required; pass --profile")
Expand Down Expand Up @@ -3984,7 +3993,7 @@ def translated(zh, en):
)
_write_fix_log(stage, fix_log)
if include_english:
from .report_i18n import write_english_report
from harness.browser_use.report_i18n import write_english_report

write_english_report(stage)
_write_fix_log(stage, fix_log, english=True)
Expand Down Expand Up @@ -4031,7 +4040,6 @@ def main() -> None:
type=Path,
default=None,
)
from harness.report_languages import add_language_argument, primary_report_language
add_language_argument(parser)
parser.add_argument("--include-english", action="store_true", help="Generate both editions; --language selects the homepage")
args = parser.parse_args()
Expand Down
4 changes: 2 additions & 2 deletions harness/browser_use/report_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from collections import Counter
from pathlib import Path

from bs4 import BeautifulSoup
from lexbench_webfetch.paths import contained_path

from harness.browser_use.failure_cause_review import (
Expand Down Expand Up @@ -39,6 +40,7 @@
from harness.reporting.assets import bound_json_asset
from harness.reporting.presentation import visible_text as _visible_text
from harness.reporting.templates import template_identity
from harness.reporting.versions import check_build_version
from harness.web_fetch.safety import reject_evidence_symlinks

FORBIDDEN_READER_TERMS = (
Expand Down Expand Up @@ -310,7 +312,6 @@ def _check_public_privacy(report_dir: Path) -> None:

def check_methodology_evidence(content: str, tasks: list[dict], environment: dict) -> None:
"""Require displayed deployment and evaluator facts to match the selected evidence."""
from bs4 import BeautifulSoup

soup = BeautifulSoup(content, "html.parser")
expected = {
Expand Down Expand Up @@ -478,7 +479,6 @@ def check(
raise ValueError("report is missing one or more required report pages")
selection = load_pilot(selection_path)
audit = json.loads(audit_path.read_text(encoding="utf-8"))
from harness.reporting.versions import check_build_version
check_build_version(report_dir, audit)
if audit.get("profile_sha256") and audit.get("template") != template_identity("browser-use"):
raise ValueError("report bypasses the maintained Browser Use template")
Expand Down
66 changes: 3 additions & 63 deletions harness/browser_use/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
from webarena_verified import WebArenaVerified
from webarena_verified.environments.container.manager import ContainerManager

from harness.browser_use.model import OpenAIResponsesModel
from harness.browser_use.selection import PilotTask, load_pilot, validate_against_official
from harness.container_isolation import require_linux_cgroup_v2
from harness.environments.browser import (
ENGINE_PORTS,
Expand Down Expand Up @@ -52,14 +54,12 @@
site_runtime_compatibility,
verify_session_viewport,
)
from harness.record_io import load_jsonl
from harness.resource_accounting import (
DockerStatsResources,
ResourceSampler,
)

from .model import OpenAIResponsesModel
from .selection import PilotTask, load_pilot, validate_against_official

ENGINE_IMAGES = configured_browser_images()

SCHEMA = "lex_browser_use_attempt/1"
Expand All @@ -70,14 +70,6 @@
# it only keeps environment preparation from aborting a formal cohort.










def _load_repo_env(repo_root: Path) -> dict[str, str]:
"""Load repository defaults, with explicit process overrides for automation."""
env_file = repo_root / ".env"
Expand All @@ -92,16 +84,6 @@ def _load_repo_env(repo_root: Path) -> dict[str, str]:
return values












def _limit_cowagent_transport_retries(executor_class: Any) -> None:
"""Limit CowAgent API errors to the initial request plus one retry."""
if getattr(executor_class, "_lex_browser_eval_retry_limit", False):
Expand All @@ -127,45 +109,6 @@ def limited(
executor_class._lex_browser_eval_retry_limit = True









































def _system_prompt() -> str:
return """You are the autonomous web agent under evaluation. Use only the browser tool.
Complete the objective from the supplied start URLs. Do not ask for help, download files, or
Expand Down Expand Up @@ -234,8 +177,6 @@ def _parse_agent_response(text: str) -> dict[str, Any] | str:
return repaired if isinstance(repaired, dict) else text




def _run_attempt( # noqa: PLR0913
*,
lock_path: Path,
Expand Down Expand Up @@ -484,7 +425,6 @@ def _load_completed_attempts(
if not path.exists():
return {}
completed: dict[tuple[str, str, int, int], dict[str, Any]] = {}
from harness.record_io import load_jsonl

for record_number, row in enumerate(load_jsonl(path, skip_blank=True), 1):
model = str(row.get("model") or expected_model or "")
Expand Down
6 changes: 4 additions & 2 deletions harness/browser_use/selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
from pathlib import Path
from typing import Any

from lexbench_webfetch.paths import contained_path

from harness.web_fetch.safety import unlinked_path

PILOT_SCHEMA = "lex_webarena_verified_pilot/1"
ALLOWED_SITES = frozenset({"shopping", "shopping_admin", "reddit", "gitlab"})
TIER_COUNTS = {"easy": 15, "medium": 30, "hard": 45}
Expand Down Expand Up @@ -331,9 +335,7 @@ def report_selection_path(
profile: dict, explicit: Path | None = None, *, authority_root: Path | None = None
) -> Path:
"""Use the same frozen selection in report construction and packaging."""
from lexbench_webfetch.paths import contained_path

from harness.web_fetch.safety import unlinked_path
binding = profile.get("task_selection") or {}
value = explicit or binding.get("path") or "evaluation/browser-use/webarena-verified-pilot.json"
path = Path(value)
Expand Down
6 changes: 3 additions & 3 deletions harness/environments/webarena/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
from webarena_verified.environments.setup import setup_init
from webarena_verified.types.task import WebArenaSite

from .config import SITES, RuntimeConfig
from .manager import EnvironmentManager
from .map_archives import verify_archives
from harness.environments.webarena.config import SITES, RuntimeConfig
from harness.environments.webarena.manager import EnvironmentManager
from harness.environments.webarena.map_archives import verify_archives


def _write_receipt(path: Path | None, payload: dict[str, Any]) -> None:
Expand Down
4 changes: 2 additions & 2 deletions harness/environments/webarena/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from typing import Any
from urllib.parse import urlparse

from harness.web_fetch.safety import unlinked_path, write_new_file

SITES = ("shopping_admin", "shopping", "reddit", "gitlab", "map", "wikipedia")
SITE_TOKENS = {site: "__" + site.upper() + "__" for site in SITES}
WEBARENA_VERIFIED_VERSION = "1.2.3"
Expand Down Expand Up @@ -151,7 +153,6 @@ def main_image_lock(argv=None) -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--output", type=Path, required=True)
args = parser.parse_args(argv)
from harness.web_fetch.safety import unlinked_path

args.output = unlinked_path(args.output)
if args.output.exists():
Expand All @@ -165,7 +166,6 @@ def main_image_lock(argv=None) -> None:
for site, reference in official_image_references().items()
},
}
from harness.web_fetch.safety import write_new_file

write_new_file(
args.output, (json.dumps(payload, indent=2, ensure_ascii=False) + "\n").encode("utf-8")
Expand Down
Loading
Loading