Skip to content

feat(interactive_browser): serve Environment Server resources sessions - #3812

Merged
ananthsub merged 1 commit into
NVIDIA-NeMo:mainfrom
waple0820:feat/browser-resources-sessions
Oct 1, 2026
Merged

ananthsub merged 1 commit into
NVIDIA-NeMo:mainfrom
waple0820:feat/browser-resources-sessions

Conversation

@waple0820

Copy link
Copy Markdown
Contributor

What

interactive_browser now serves the resources-session protocol an Environment Server uses (#3717): a typed /seed_session under the caller's resources_session_id, and a /close_session that actually releases the browser.

This is the follow-up I mentioned when closing #2612.

Why

Two gaps under Environment Server episodes today:

  1. Typed seed is swallowed. BrowserSeedSessionRequest allows extra fields, so a ResourcesSeedSessionRequest body validates as a legacy seed and gets an empty {} back. single_agent_turn then fails validating the seed response.
  2. Close is a no-op. /close_session falls through to the base default, so an episode that ends before verify (timeout, cancelled agent) keeps its browser. With a remote provider that is a paid session held until the provider reclaims it.

How

Same shape as swebench_pro:

  • seed_session takes Union[ResourcesSeedSessionRequest, BrowserSeedSessionRequest] with union_mode="left_to_right", so the typed body wins. It opens the browser from task_data under the caller's id and binds the cookie to that id, so direct-HTTP tool calls and the MCP token both land on that browser. A retried seed for the same (episode_id, task_id) returns the existing session; a different episode on the same id is rejected.
  • close_resources_session releases the browser, is idempotent, checks episode_id, and remembers closed ids. A seed that arrives after its close is rejected, so it can't open a browser nobody will close. An empty body still closes the cookie session for /run callers.
  • A per-id lock serializes seed and close. A close that arrives while the browser is still opening waits for it, then releases it.
  • num_workers other than 1 is refused, since browsers live in process.

Legacy /run seeding is unchanged and still returns {}.

Tests

tests/test_resources_sessions.py (15 new): typed seed over HTTP, tool calls reaching the seeded browser over direct HTTP and over MCP, retried seed, id reuse across episodes, close before verify, double close, mismatched episode, malformed close (422), legacy seed and empty close, close during a slow seed, seed after close, num_workers guard, verify after close.

cd resources_servers/interactive_browser && pytest tests -q   # 49 passed
pytest tests/unit_tests/test_mcp_auto_exposure.py tests/unit_tests/test_base_resources_server.py -q   # 60 passed

Not in this PR

Idle expiry for sessions whose Environment Server never closes them (process killed mid-episode). I'll ask on #3037 whether that belongs in the base class or per server before opening anything.

🤖 Generated with Claude Code

An Environment Server seeds with a typed body under its own
resources_session_id and closes that id through /close_session. The
browser server accepted any body as its legacy seed spec (extra fields
allowed), so a typed seed was swallowed and answered with an empty
response the Environment Server rejects, and /close_session fell through
to the base no-op, so an episode that ended before verify kept its
browser open.

- seed_session tries the typed body first, opens the browser from
  task_data under the caller's id, binds the cookie to that id, and
  treats a retried seed for the same episode and task as a no-op.
- close_resources_session releases the browser, is idempotent, checks
  the episode, and remembers closed ids so a seed that lands after its
  close opens nothing. An empty body still closes the cookie session.
- Per-id locks make a close that arrives while the browser is opening
  wait for it and then release it.
- num_workers other than 1 is refused, since browsers live in process.

Follows the pattern swebench_pro uses for the same protocol.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: waple0820 <232305951+waple0820@users.noreply.github.com>
@copy-pr-bot

copy-pr-bot Bot commented Sep 30, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@nemo-automation-bot nemo-automation-bot Bot added the community-request Issue reported or requested by someone from the community label Sep 30, 2026
@yaoyu-33 yaoyu-33 added area:environment Individual environments, benchmarks, verifiers, and environment-specific resources servers complexity:low Localized change in one scope with a small, straightforward review surface feature New capabilities, enhancements, or enablement work needs-review PR is ready for code review and waiting on a reviewer labels Sep 30, 2026
@github-actions github-actions Bot added the sla:triage-overdue Review assignment is over the one-business-day SLA label Oct 1, 2026
@ananthsub

Copy link
Copy Markdown
Contributor

/ok to test d41efff

@ananthsub
ananthsub added this pull request to the merge queue Oct 1, 2026
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

🔄 Merge queue validation started! You can track the progress here: https://github.com/NVIDIA-NeMo/Gym/actions/runs/36910298857

Merged via the queue into NVIDIA-NeMo:main with commit 0a7b3c4 Oct 1, 2026
47 checks passed
katherineh123 pushed a commit to katherineh123/Gym that referenced this pull request Oct 2, 2026
…IDIA-NeMo#3932)

## What

Small follow-up to NVIDIA-NeMo#3812. A browser seeded by an Environment Server now
carries its episode (`rollout_id`, `attempt`) to the session provider,
and `remote_cdp` logs the provider's session id together with it when
the session is acquired.

## Why

Provider sessions were tagged with `resources_session_id` only. Under an
Environment Server that id is minted inside `single_agent_turn` and
never reaches the episode result, so it can't join a leaked cloud
session back to the rollout that held it. The episode id is on both
sides: it comes in with the seed and goes out on the result.

The log line covers providers that ignore metadata: the join still
exists in the environment's own log.

## Changes

- Typed seed adds `rollout_id` and `attempt` to the backend's session
metadata. Legacy `/run` seeds are unchanged (no episode to tag).
- `remote_cdp` logs `Provider 'x' session <id> acquired for {...}` on
acquire, and includes the metadata in the release-failure warning.
- README: one paragraph under Session lifetime.

## Tests

Three new tests, failing before the change: typed seed passes the
episode in metadata, legacy seed doesn't, and the acquire log line names
both the provider session and the rollout.

```
cd resources_servers/interactive_browser && pytest tests -q   # 52 passed
```

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: waple0820 <232305951+waple0820@users.noreply.github.com>
Co-authored-by: waple0820 <232305951+waple0820@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

This branch was previously deployed

1 inactive deployment
public — d41efffc Deployed Oct 1, 2026 by copy-pr-bot[bot] via release / finalize / notify #4103
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:environment Individual environments, benchmarks, verifiers, and environment-specific resources servers community-request Issue reported or requested by someone from the community complexity:low Localized change in one scope with a small, straightforward review surface feature New capabilities, enhancements, or enablement work needs-review PR is ready for code review and waiting on a reviewer sla:triage-overdue Review assignment is over the one-business-day SLA

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants