Skip to navigation

nemo_gym.agents.utils.sandbox_session

View as Markdown

Supervised harness activations over one retained sandbox connection.

Module Contents

Classes

NameDescription
ExpiryUnsupportedErrorexpire cannot stop this running harness: its sandbox’s uploads can wait for capacity the harness holds.
SandboxCommandHarness argv, run under the bash process supervisor.
SandboxSessionExecute, stop, capture, then release an owned or borrowed sandbox.

Functions

NameDescription
_reuse_or_start-
shell_assignmentsBash source assigning each value, for sandbox runners that cannot parse JSON.

Data

HARNESS_COMMAND_NOT_STARTED

HARNESS_RUNNER_FAILED

HARNESS_RUNNER_FILE

HARNESS_RUNNER_SCRIPT

LOG

_SHELL_NAME

API

class nemo_gym.agents.utils.sandbox_session.ExpiryUnsupportedError()
Exception

Bases: Exception

expire cannot stop this running harness: its sandbox’s uploads can wait for capacity the harness holds.

class nemo_gym.agents.utils.sandbox_session.SandboxCommand(
argv: list[str]
)
Dataclass

Harness argv, run under the bash process supervisor.

argv
list[str]
class nemo_gym.agents.utils.sandbox_session.SandboxSession(
session_dir: str,
workdir: str | None,
harness: str,
owns_sandbox: bool = False
)
Dataclass

Execute, stop, capture, then release an owned or borrowed sandbox.

Runtime installation belongs in the adapter’s seed hook. stage_activation stages input and returns a command; collect copies harness artifacts to the agent server after cleanup is confirmed. It may run a bounded snapshot command, but must not restart the harness. Parsing responses stays in the adapter.

Each activation stages input and a fresh supervisor, runs the harness process, then stops and collects artifacts. Adapters may explicitly rearm it with begin_activation after confirmed cleanup, retaining native state outside the per-activation directory. The default still permits only one launch. Execution retains the sandbox until close releases it. Missing termination evidence remains retryable. A final negative receipt skips capture, releases a borrowed connection and leaves teardown to Resources; an owned sandbox falls back to provider stop. Closed means release succeeded.

The supervisor stops the harness process group; a forced stop also reaches the harness’s descendants in other groups and processes that inherited its NEMO_GYM_SUPERVISOR marker. Processes that had left the harness when the stop began, processes in other groups after a natural exit, and a process that left between two scans without the marker remain until the sandbox owner destroys the sandbox. Borrowed sandboxes stay available for Resources verification; owned sandboxes are stopped on close. Unconfirmed harness termination prevents capture and verification.

Each stop, collection and release phase is bounded by close_timeout (or the timeout passed to close). Failed cleanup/release retains the handle for retry. Failed capture is recorded separately and does not prevent release. HTTP request binding and activation retries belong to the agent session layer.

_capture_attempted
bool = field(default=False, init=False)
_close_task
Task[None] | None = field(default=None, init=False)
_collect
Callable[[], Awaitable[Artifacts]] | None = field(default=None, init=False)
_exec_task
Task[SandboxExecResult] | None = field(default=None, init=False)
_expire_requested
bool = field(default=False, init=False)
_expire_write
Task[None] | None = field(default=None, init=False)
_finalize_task
Task[None] | None = field(default=None, init=False)
_session_root
str | None = field(default=None, init=False)
_stage_task
Future[SandboxCommand] | None = field(default=None, init=False)
_unconfirmed_expire_reason
str | None = field(default=None, init=False)
_unconfirmed_expiry
bool = field(default=False, init=False)
artifacts
Artifacts | None = field(default=None, init=False)
capture_error
Exception | None = field(default=None, init=False)
cleanup
CleanupReceipt | None = field(default=None, init=False)
cleanup_confirmed
bool

Whether no launch occurred, termination was witnessed, or owned sandbox stopped.

closed
bool

Whether sandbox release completed successfully.

closing
bool

Whether close has been requested, including a failed attempt.

execution_oom
NgFailureEvent | None = field(default=None, init=False)
expire_reason
str | None = field(default=None, init=False)
expired
bool

Whether expire ended this activation; its receipt then records the timeout.

expired_before_launch
bool

Whether expire kept the harness from starting, so it left no output to collect.

harness
str
launch_started
bool = field(default=False, init=False)
owns_sandbox
bool = False
sandbox
AsyncSandbox
sandbox_stopped
bool = field(default=False, init=False)
session_dir
str
stop_request_path
str

Marker path an adapter may pass to its harness for cancellation diagnostics.

termination_error
NgFailureEvent | None = field(default=None, init=False)
workdir
str | None
async
nemo_gym.agents.utils.sandbox_session.SandboxSession._close(
timeout: float
) -> None
async
nemo_gym.agents.utils.sandbox_session.SandboxSession._confirm_unacknowledged_expiry(
timeout: float
) -> None
async

Keep a stop whose marker write failed only if the marker is in place and the receipt timed out.

A write still in flight is awaited first, so a failure that arrives during finalization is checked too. The whole check, including any wait for provider capacity, is bounded by timeout; without a confirmation the stop stays unattributed and finalization goes on. That includes a write still in flight when the bound runs out: the harness may have ended at its own deadline, before any marker arrived.

nemo_gym.agents.utils.sandbox_session.SandboxSession._finalize(
timeout: float
) -> None
async
nemo_gym.agents.utils.sandbox_session.SandboxSession._finish(
timeout: float
) -> None
async

Join process finalization without releasing the sandbox connection.

nemo_gym.agents.utils.sandbox_session.SandboxSession._release_exec(
timeout: float
) -> None
async
nemo_gym.agents.utils.sandbox_session.SandboxSession._request_expiry(
timeout: float
) -> None
async

Write the marker the supervisor watches, within timeout including any wait for the provider.

A file write, not an exec: a provider that bounds its concurrent execs (Local) counts the harness’s own launch against that bound for as long as it runs, and a stop must not wait for the harness to end.

nemo_gym.agents.utils.sandbox_session.SandboxSession._stage(
stage_activation: collections.abc.Callable[[], collections.abc.Awaitable[nemo_gym.agents.utils.sandbox_session.SandboxCommand]]
async
nemo_gym.agents.utils.sandbox_session.SandboxSession._write_expiry(
timeout: float,
reason: str | None
) -> None
async
nemo_gym.agents.utils.sandbox_session.SandboxSession.begin_activation(
session_dir: str,
timeout: float
) -> None
async

Rearm a stopped harness using fresh supervisor files in the same sandbox.

The adapter retains its native conversation elsewhere in the sandbox. A previous launch must be reaped before any activation fields can be reset. Close fences all later activations, including when release needs retry.

nemo_gym.agents.utils.sandbox_session.SandboxSession.close(
timeout: float
) -> None
async

Join finalization before release; concurrent/retried closes never duplicate capture.

stage_activation: collections.abc.Callable[[], collections.abc.Awaitable[nemo_gym.agents.utils.sandbox_session.SandboxCommand]],
collect: collections.abc.Callable[[], collections.abc.Awaitable[nemo_gym.agents.utils.sandbox_session.SandboxSession[Artifacts]]],
timeout: float,
close_timeout: float,
deadline: float | None = None
async

Run once, preserving captured artifacts even when execution raises.

Cancellation stops the remote harness before releasing provider exec. Callers that merely stop waiting (such as disconnected HTTP requests) must shield their shared activation task.

nemo_gym.agents.utils.sandbox_session.SandboxSession.expire(
timeout: float,
reason: str | None = None
) -> bool
async

End the harness as at its time limit, so its receipt records timed_out.

Unlike stop_harness (a cancellation), the activation is then graded like a timeout. A request made before launch ends the harness as it would launch. Returns False when the activation already finished, the session is closing, or expiry was already requested.

Stopping a running harness takes an upload, which on a provider without SupportsSandboxIndependentUploads can wait behind the very commands it must stop, so there it raises ExpiryUnsupportedError. A request before launch works on every provider.

nemo_gym.agents.utils.sandbox_session.SandboxSession.read_output_log() -> str
async

Read combined supervisor/harness diagnostics without masking the original failure.

nemo_gym.agents.utils.sandbox_session.SandboxSession.stop_harness(
timeout: float
) -> None
async

Confirm harness termination before capture and transport release.

nemo_gym.agents.utils.sandbox_session.shell_assignments(
values: collections.abc.Mapping[str, str | collections.abc.Sequence[str] | collections.abc.Mapping[str, str]]
) -> str

Bash source assigning each value, for sandbox runners that cannot parse JSON.

A string becomes a scalar and a sequence an array. A mapping becomes an array of KEY=VALUE words, ready for export or env. Every word is shell-quoted. Words must be strings without NUL, which bash cannot hold.

nemo_gym.agents.utils.sandbox_session.HARNESS_COMMAND_NOT_STARTED = (126, 127)
nemo_gym.agents.utils.sandbox_session.HARNESS_RUNNER_FAILED = 125
nemo_gym.agents.utils.sandbox_session.HARNESS_RUNNER_FILE = 'harness_runner.sh'
nemo_gym.agents.utils.sandbox_session.HARNESS_RUNNER_SCRIPT = Path(__file__).with_name(HARNESS_RUNNER_FILE)
nemo_gym.agents.utils.sandbox_session.LOG = logging.getLogger(__name__)
nemo_gym.agents.utils.sandbox_session._SHELL_NAME = re.compile('[A-Za-z_][A-Za-z0-9_]*')