Repository navigation
feat(browsecomp_agent): run the session harness inside a sandbox - #5489
Merged
Merged
Conversation
An Environment Server seeds with ResourcesSeedSessionRequest and closes with /close_session; the harness accepted neither, and its per-rollout state (the per_session page workspace and the provider-call metrics) was released only by /verify, and the metrics not at all. - /seed_session takes the typed request, keyed by resources_session_id, and still takes an agent /run's flat row under the cookie session. A typed seed is refused with 422 when task_data lacks question or ground_truth, or when a workspace ID is not a plain directory name; a seed for another episode or after the close gets 409. - /close_session removes the workspace and the metrics, idempotently, through ResourcesSessionFence. A failed removal raises and keeps the session for a retried close. - A tool call for a closed session gets 404 (session_lost) instead of recreating its workspace. - /verify drops the session's metrics entry after reporting it, unless dump_session_id_to_metrics_on_exit keeps it for the shutdown dump. Refs GYMINT-161. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
browsecomp_agent ran only through its own /run behind the legacy_agent relay. It now serves agent sessions, so an Environment Server can drive it against browsecomp_advanced_harness in steps. - The seed must grant exactly one direct HTTP tool access and no sandbox or required MCP access; anything else is refused with a final 422. - In a session, tool calls go to the grant's URL with its headers and cookies, and the close returns the resources cookies for /verify. The context compaction, resets, progress board and deadline are the same loop as on the /run path. - max_run_retries retries an empty final answer inside the one activation, keeping the harness session, as /run does. - A model call that fails with an HTTP error returns a failed response with model_request_error and its status, for the Environment Server's outcome policy; other errors fail the activation with a 500. - The harness's /verify scores a timed-out rollout 0 without the judge, as /run did by not verifying it, and every row reports timed_out, which only /run added before. - benchmarks/browsecomp/config_single_agent_turn.yaml runs the benchmark through single_agent_turn_legacy; config.yaml keeps the relay. Refs GYMINT-211. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
Contributor
Author
|
/ok to test 65c7c1c |
Every server under the resources session contract answers a verify for a closed session with lost_session_verification, including those whose reward reads only the response, so a closed session reads the same everywhere. single_agent_turn verifies before it closes, so the mask only catches an out-of-order verify. TavilySearchVerifyResponse now inherits BaseVerifyResponse so it can carry mask_sample and failure_kind. A session the fence never recorded as closed, such as an agent's /run or a caller that never seeds, is graded as before. Refs GYMINT-161. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
…gymint-211-browsecomp-agent-sessions Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
The verify of a closed session now returns a masked sample before the judge runs, so read timed_out first and put it on that row too. Every row keeps reporting whether the agent stopped at rollout_timeout_s. Refs GYMINT-211. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
Contributor
Author
|
/ok to test 615a506 |
This was referenced Nov 4, 2026
browsecomp_agent now seeds agent sessions, so the registry test requires it, and an --agent-type swap to it keeps the session environment server instead of relaying. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
…from nemo_gym.server_runtime main removed nemo_gym.testing.session_conformance; the check now lives in the server_runtime facade. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
…rowsecomp_agent sessions branch Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
Contributor
Author
|
/ok to test 5dd4dd1 |
…able sandbox config and loopback token counting - An Enroot isolation probe that times out or hits a provider error now fails the seed as retryable. Before, it was refused for good with 422. A probe that answers with a shared namespace is still refused. - An unknown provider or an unknown sandbox_config key refuses the seed with 422 instead of a retryable 500. - The seed checks this agent's own token-count URL for loopback, as it already did for the model and tool URLs. - The sandboxed loop's deadline counts from the runner's start, so the endpoint checks spend the rollout's budget, not the supervisor's 30-second grace. - Test the host activation path (deadline clamp, payload, error receipt), and drop a cluster name from a test comment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
Contributor
Author
|
/ok to test 1adaa74 |
hemildesai
previously approved these changes
Nov 5, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
… mask until close - Only the supervisor's deadline answers a shell command with [exit_code=-1]. A provider timeout (docker cp, the exec, reading the output) is a sandbox failure: [exit_code=-2] and a masked sample. - /verify of an Environment Server session no longer releases it; the close does. A re-sent or retried verify still finds the shell failure and the metrics. A judge failure on a masked sample returns the masked sample instead of raising, on both paths. - A typed seed whose provider cannot be created, or whose Enroot sandbox shows the host's PID namespace or a mismatched /proc mount, answers 422 once the sandbox is stopped. A probe that does not finish stays retryable. - After a failed start, later commands answer [exit_code=-2] without starting another sandbox. - The shell runner writes UTF-8 with errors="replace", so a lone surrogate no longer loses the output and its exit-code envelope. - The sandbox timeouts must be finite. - README: shutdown cleanup is best-effort; verify and close ownership. - Docstrings for seed_session, bash_command and verify, and tests for the failure paths above. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
Base automatically changed from
yuya/gymint-161-browsecomp-harness-sessions
to
main
November 5, 2026 05:55
…ad server off the network - The sandboxed loop's model, tool and token-count calls stop resending a refused connection after about 90 seconds. The error reaches the runner's receipt, so the activation fails as retryable instead of running into the rollout deadline and scoring as a timed-out zero. - The benchmark and example configs set absolute_ip_server_types to the model, resources and agent servers, so the head server, whose config endpoint returns the merged config, stays on loopback. - update_progress arguments that are valid JSON but not an object answer the model with a tool error instead of failing the activation. - The Enroot probe follows the resources server's rule: only a finished probe that shows the host's namespace or a mismatched /proc mount refuses the seed; a non-zero exit or unparsable output is retryable. - Upstream error text in a failed response keeps its first 4000 characters, and a missing receipt reports the runner log's last 4000. - The sandbox timeouts must be finite. - Removed the unreachable snap_dir branch of the session loop and the config_single_agent_turn.yaml alias, which main never had. - README: loopback URLs only warn under Docker; lost connections. - Docstrings for responses and run; tests for the paths above, the loopback tool grant, receipt parsing, a cancelled waiter and the token-count endpoint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
khazic
pushed a commit
to khazic/Gym-lao
that referenced
this pull request
Nov 5, 2026
…sessions (NVIDIA-NeMo#5484) * feat(browsecomp_advanced_harness): serve typed resources sessions An Environment Server seeds with ResourcesSeedSessionRequest and closes with /close_session; the harness accepted neither, and its per-rollout state (the per_session page workspace and the provider-call metrics) was released only by /verify, and the metrics not at all. - /seed_session takes the typed request, keyed by resources_session_id, and still takes an agent /run's flat row under the cookie session. A typed seed is refused with 422 when task_data lacks question or ground_truth, or when a workspace ID is not a plain directory name; a seed for another episode or after the close gets 409. - /close_session removes the workspace and the metrics, idempotently, through ResourcesSessionFence. A failed removal raises and keeps the session for a retried close. - A tool call for a closed session gets 404 (session_lost) instead of recreating its workspace. - /verify drops the session's metrics entry after reporting it, unless dump_session_id_to_metrics_on_exit keeps it for the shutdown dump. Refs GYMINT-161. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com> * fix(browsecomp_advanced_harness): mask a verify after its session closed Every server under the resources session contract answers a verify for a closed session with lost_session_verification, including those whose reward reads only the response, so a closed session reads the same everywhere. single_agent_turn verifies before it closes, so the mask only catches an out-of-order verify. TavilySearchVerifyResponse now inherits BaseVerifyResponse so it can carry mask_sample and failure_kind. A session the fence never recorded as closed, such as an agent's /run or a caller that never seeds, is graded as before. Refs GYMINT-161. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com> * test(browsecomp_advanced_harness): import the session contract check from nemo_gym.server_runtime main removed nemo_gym.testing.session_conformance; the check now lives in the server_runtime facade. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com> * fix(browsecomp): isolate workspace tools in owned sandboxes Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com> * fix(browsecomp): require Enroot process isolation before tools Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com> * fix(browsecomp_advanced_harness): start /run sandboxes on first use; keep the shell's error envelope - benchmarks/browsecomp/config.yaml kept workspace: per_session with no sandbox_provider, so every seed got 422 until NVIDIA-NeMo#5489 lands. Give the benchmark's resources server the Docker provider, as NVIDIA-NeMo#5489 does. - An agent's /run can end without /verify (browsecomp_agent does after a timeout), and only /verify releases that path's sandbox. Start the /run sandbox with the first bash_command instead of at the seed, so only a run that used the shell can hold one, and a failed start no longer strands the seed's workspace (the failed seed's cookie was dropped, so nothing could release it). Document the remaining gap. - A command killed at the supervisor deadline, or a provider exec failure, answered HTTP 500. Answer with the host interpreter's [exit_code=-1] / [exit_code=-2] envelopes, as the README promises, and log the detail. - Say that only Enroot is probed for isolation, and that providers such as Apptainer bind-mount the host's home and /tmp by default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com> * fix(browsecomp_advanced_harness): answer and mask sandbox start failures; refuse bad sandbox config - A /run shell sandbox that cannot start now answers the [exit_code=-2] envelope instead of HTTP 500 with the provider's error text, and a blocked command answers before any sandbox starts. - A session whose tool sandbox failed to start or run a command gets a masked sample at /verify (failure_kind provider_unavailable). The judged reward is kept. - A command the supervisor ends at its deadline answers [exit_code=-1], as the README says, instead of -2. - A missing provider block or an unknown sandbox_config key refuses the seed with 422 instead of a retryable 500. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com> --------- Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Main now carries #5484 as a squash commit, so the files this branch's #5484 fixes touch conflict in overlapping hunks; this branch's side is #5484 plus those fixes and is kept. The agent picks up main's rename of VLLMConverter to ChatCompletionsConverter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
Contributor
Author
|
/ok to test 74ea259 |
hemildesai
approved these changes
Nov 5, 2026
kajalj22
approved these changes
Nov 5, 2026
Contributor
|
🔄 Merge queue validation started! You can track the progress here: https://github.com/NVIDIA-NeMo/Gym/actions/runs/42077542830 |
This branch was previously deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
BrowseComp's agent sessions previously ran the research loop on the Agent Server host. The session path now launches the same repository loop inside a supervised sandbox, and both shipped pairings use
single_agent_turn_legacy.Execution and lifecycle
/runand unseeded/v1/responsesrequire explicitallow_host_execution: true./procmount before staging rollout input. A shared host namespace or failed probe assertion refuses the seed with 422; a probe that times out or hits a provider error fails it as retryable. Upstream Enroot 4.2+ is required.SandboxSessionsupervises execution, collection and release. Failed startup/teardown retains state for cleanup retry, including failed file delivery followed by failed provider stop.Compatibility and capability
The adapter declares Evaluation capability. Close always returns an observation bundle with
native_turn_evidence_unavailableorobservation_capture_failed; native invocation/turn records remain unimplemented. Model-call capture and Responses output remain available. Function-calling shell conformance is applicable but optional at this level and is not registered or qualified. No Observability, Training, SWE-bench or Terminal-Bench qualification is claimed.Sampling, progress, compaction, resets and empty-answer retry behavior stay in the existing loop. Hosted endpoints without
/tokenizemust explicitly disablesave_model_call_using_vllm_tokenize_endpoint; sandbox sessions reject hostsnap_dir.The default sandbox activation cap is 3600 seconds and also bounds the loop when
rollout_timeout_sis unset. Session outcome policy can exclude/retry errors that the former host/runcounted as zero-reward answers. Compare coverage and failure rows alongside score. Timed-out responses still score zero without invoking the judge. Tasks, prompts and answer grading were not changed.No separate issue is needed for this focused migration of the existing BrowseComp integration.
Validation
responses_api_agents/browsecomp_agent/.venv/bin/pytest responses_api_agents/browsecomp_agent/tests -q: 116 passed, including a real subprocess/HTTP boundary test and owned-start failure/cleanup retry.resources_servers/browsecomp_advanced_harness/.venv/bin/pytest resources_servers/browsecomp_advanced_harness/tests -q: 226 passed.pre-commit run --all-files: passed; focused checks also passed after the bootstrap fix.npm run checkinfern/: 0 errors, 1 warning.After merging the updated #5484 and two review passes, at
1adaa74def1d(macOS, no Docker, so no sandbox smoke for these changes):pytest responses_api_agents/browsecomp_agent/tests -q: 126 passed. The tests added in these passes fail on the previous code:ConnectionErrorreceipt within seconds (before: it spun untilrollout_timeout_s).sandbox_configkey.pytest resources_servers/browsecomp_advanced_harness/tests -q: 234 passed.pytest tests/unit_tests/tooling/test_component_dependencies.py tests/unit_tests/test_public_api_facades.py tests/unit_tests/config/test_global_config.py: 1041 passed.scripts/component_locks.py: 269 projects, 0 failed (main'sopencode_searchlock fix is merged in).pre-commit run --fileson the changed files: passed.Review fixes on
74ea2592bc00(third review pass)Merged
main(now with #5484 as a squash commit, plus #5528'sChatCompletionsConverterrename) and fixed what a third review found.browsecomp_advanced_harness(code from #5484):[exit_code=-1]. A provider timeout (docker cp, the provider's exec, reading the output) is a sandbox failure:[exit_code=-2]and a masked sample (provider_unavailable). Before, anyTimeoutErrorread as the model's own timeout and was scored./verifyof an Environment Server session no longer releases it;close_sessiondoes. A re-sent verify (the client resends after a dropped connection) or a retry after a judge failure therefore still finds the shell failure and the metrics; before, the retry came back unmasked with empty metrics. A judge failure on a masked sample returns the masked sample instead of raising. An agent's/runsession is still released by its verify./procmount, answers 422 once the sandbox is stopped (was a retryable 500 for every episode). A probe that does not finish stays retryable. The agent applies the same rule.[exit_code=-2]without starting another sandbox.errors="replace", so a lone surrogate in the output no longer loses the whole result and its exit-code envelope.server_shutdown_timeout_secondsis raised, and Enroot ignoresttl_s).browsecomp_agentand configs (this PR):use_absolute_ipis scoped withabsolute_ip_server_types: [responses_api_models, resources_servers, responses_api_agents]in both shipped configs, so the head server, whose unauthenticated config endpoint returns the merged config including API keys, stays on loopback.update_progressarguments that are valid JSON but not an object get a tool error back, like invalid JSON; before, they crashed the activation into a retried, unscored row.snap_dirbranch of the session loop andconfig_single_agent_turn.yaml, an alias thatmainnever had (config.yamlalready runs this path).responsesandrun.Validation at
74ea2592bc00(macOS, no Docker):pytest responses_api_agents/browsecomp_agent/tests: 147 passed.pytest resources_servers/browsecomp_advanced_harness/tests: 246 passed. The new tests fail on the previous code (provider timeouts, re-sent verify, 422 refusals, commands after a failed start, the lone-surrogate runner, endpoint loss mid-episode, non-objectupdate_progressarguments).tests/unit_tests/config/test_global_config.py,test_public_api_facades.py,test_session_contract_repo.py,benchmarks/browsecomp/tests: 1052 passed.tests/unit_tests/environment/test_benchmarks.py::test_every_repo_benchmark_appears_in_listingfails on this workstation only because its hostname does not resolve foruse_absolute_ip; it fails the same way onmain.pre-commit run --fileson the changed files: passed.Not run: no sandbox smoke (Docker or Enroot) at this head; the last real pairing runs below predate the second and third review passes. The earlier CI failure
responses_api_models/vllm_model/tests/test_framework_context.py::test_cancelled_chat_keepalive_retains_uncertain_capture[False]is a timing flake that reproduces onmain(about 1 in 10 runs locally); these PRs do not touch that server.Real pairing evidence
Functional cluster run
smoke-04(job6446261), folderssmoke-04/{browsecomp,reasoning}, atbe3925b94589eca0685dcd746fda50492476d2dd: five unchanged BrowseComp tasks completed scoring (0/5 correct), plus two unchanged reasoning_gym examples completed through configuration alone (2/2 correct). Policy:nvidia/nemotron-3.5-bolt-e4b; independent judge:z-ai/glm-5.3-flash; real Tavily search/browse. Policy settings: 24 steps, 360-second loop, 480-second runner, 2048 output tokens per call, temperature 0, concurrency 1. The unchanged verifier sets the judge to 10240 output tokens and temperature 1.0; captured requests, not the overridden YAML values, are the effective settings.The run exercised 22 searches, 3 browse calls, 20 shell calls and 26 progress updates. All seven usage totals matched captured policy calls and all agents closed before verification. No timeout, infrastructure failure or masked sample occurred. Health reported 0 unhealthy and 7 unobserved rows because native turn/invocation and model-call ownership records are absent. Strict evidence exited 1: TE-3 (steps), TE-6 (absent
evaluation_completedand sandbox observation records), TE-8/9 (joins), and additionally TE-5 (tools) for BrowseComp. These are explicit Evaluation limits; raw captured calls and separate runtime audits supply the smoke evidence. Supervisor and owned containers cleaned up; task credential copies were removed and the artifact scan found no exact credential values.Isolation correction: system Enroot 3.4.1 ignores the PID-isolation setting, so
smoke-04is functional evidence only. Actual final-source diagnostics on the cluster confirmed a shared host PID namespace and successful signal-0 visibility of a nonsecret host sentinel (environment reading denied). Both BrowseComp guards correctly rejected it. Complete task-local Enroot 4.2.1 (ead3a25ed974948235d28f99fc387ac82435ce62) provided distinct PID/user/mount namespaces, matching container self/PID1, and an absent/unreadable sentinel with signal 0 returningProcessLookupError; both guards accepted it.Final-source run
smoke-05(job6446620), ataebd6b9184909d1f84390205a59e07dd5d4d471b, completed the first unchanged row from each pairing on Enroot 4.2.1: BrowseComp 0/1 correct and reasoning_gym 1/1 correct, with no infrastructure failure. The smaller post-fix scope fits a 15-minute allocation; it retains the earlier 5+2 results without combining repeated tasks into an accuracy estimate. Natural BrowseComp tools: 4 searches, 1 browse, 5 shell calls and 6 progress updates. Both rows passed stock health checking with 0 unhealthy and 2 unobserved rows; strict evidence exited 1 for the same declared gaps. Captured usage matched all 18 policy calls (17 BrowseComp, 1 reasoning); the separate judge made 1 call. Agent close preceded verification, supervisor exited 0, and owned container inventories were empty. Final exact-value scan found 0 credential occurrences across 6,428 task text artifacts; temporary cluster keys were deleted. Live agent/PID1 audits and source hashes matched; this targeted run did not catch the short-lived shell runner itself. All 5 shell outputs were retained (4 exit 0, one grep exit 1, whose exact semantic cause is unclassified).The first isolation diagnostic stopped before model calls because its diagnostic script did not handle a denied
/proc/1read; the corrected retry retained that failure and kept the production source and strict acceptance conditions unchanged. This PR is ready for maintainer CI/review; it has not been merged.Earlier failed configuration and fresh-runtime ANTLR attempts are retained in validation records. Smoke used Tavily with an explicit empty additional-domain filter (
{"notices": []}); Exa was not exercised. No accuracy baseline or score-parity claim is made.