Repository navigation
Conversation
Give cline_agent the agent-session lifecycle the merged sandboxed peers have.
Seed connects to the borrowed task sandbox (or starts an agent-owned one when
sandbox_provider is set) and installs the pinned Cline 3.0.55 there: its
self-contained glibc binary from @cline/cli-linux-{x64,arm64}, checked against
a SHA-512 the adapter pins, so the sandbox needs no Node. Activation writes
Cline's provider settings (rollout-prefixed Model Server URL, x-session-id
header) and runs it under the shared process supervisor; close stops it
before verification and returns an observation bundle.
The host /run and sessionless /v1/responses path is unchanged, except that it
now installs Cline on first use instead of at server startup.
configs/cline_agent.yaml becomes the session config; the host config moves to
configs/cline_local_agent.yaml with the same agent key.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
…ne-sessions-0f4869 Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
|
/ok to test c7b36e2 |
The secrets detector reads the SHA-512 digests of the pinned npm tarballs as high-entropy strings. They are public npm integrity values, not credentials. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
|
/ok to test d533de1 |
…ne-sessions-0f4869 Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com> # Conflicts: # responses_api_agents/cline_agent/requirements.txt
|
/ok to test 98ed914 |
Review follow-ups for the Cline agent-session adapter:
- A rollout stop that lands before launch, or before the runner opens
Cline's output, now yields an empty transcript graded as a stop instead
of failing the activation.
- An agent-owned sandbox gets a TTL (installation + run + 10 minutes,
unless sandbox_config sets one) and the provider's default metadata plus
gym-agent, gym-session and gym-rollout labels.
- The activation status comes from Cline's final run_result only; a done
event without one is a crash, not a completed run.
- "Model returned empty response" is classified as agent_invalid_output.
- A developer message counts as the system message, metadata={} is
accepted, and the argv limit applies to the padded prompt.
- cline_agent.yaml sets concurrency: 32; cline_local_agent.yaml gets the
SPDX header.
- README: the Terminal-Bench 2.1 recipe is the unsupported development
pairing the smoke runs used; document Cline's checkpoint refs, what
stays in the sandbox after close, the host path's prompt, concurrency,
timed-out usage and the 300 s request limit. List Cline in the harness
docs' Tools and Sandboxed harnesses tables.
- Tests: real staging, runner and capture with a detached stdout holder,
rollout stops before and after launch, a closing session's 409, the
owned-sandbox spec, and more seed refusals.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
Cline 3.0.55 does not bound a --json run with --timeout; the subprocess deadline ends the host run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
|
/ok to test 0fb78fd |
|
🌿 Preview your docs: https://nvidia-preview-pr-5546-01e0bbbeefd1.docs.buildwithfern.com/nemo/gym |
svcnvidia-nemo-ci
left a comment
There was a problem hiding this comment.
SHIP WITH CARE
I found one RISK: concurrent installers can remove a runtime another session has just published. The inline finding describes the failure, repair invariant and regression checks.
I inspected all 17 changed files and traced the affected session, model-capture, outcome, configuration and host-call contracts. One delegated report wasn't available; I independently covered its configuration, host compatibility, dependency and documentation assignment.
This was a static review. I didn't execute tests or rollouts, and the supplied real-model smokes predate the final fixes.
Review generated by codex. This was a full review. Request a full review with /review reset=true.
| # that another installer published meanwhile is kept; only a stale or partial one is replaced. | ||
| if ! mv -T -- "$staging" "$runtime" 2>/dev/null; then | ||
| if ! ready; then | ||
| rm -rf -- "$runtime" |
There was a problem hiding this comment.
RISK — Concurrent replacement can delete a newly published runtime
Failure: With an existing partial runtime, two sessions installing the same version into one sandbox can both fail the initial mv -T and both pass ! ready. Installer A then removes the partial directory, publishes its verified runtime and returns successfully. Installer B subsequently executes this rm -rf, deleting A's ready runtime. If A's activation starts before B's rename, Cline cannot launch and the activation fails with no run result. The publication branch therefore doesn't provide the concurrent publication guarantee its comment describes. The blast radius is sessions sharing that runtime path.
Correction: Could you serialize the final readiness check, stale removal and publication using a per-runtime lock outside the replaced directory, and recheck readiness after acquiring it? Downloads and checksum verification can remain concurrent. The invariant is that an installer must never remove a verified runtime another installer published. The lock also needs bounded waiting and release on failure. The available sandbox tools don't establish a portable locking implementation, so I'm not suggesting an unchecked shell patch.
Preservation and acceptance: Add a proposed deterministic regression with a partial runtime and two installers. Use barriers so both reach stale removal, let A publish, then release B. After A publishes, an executable probe must never find the runtime missing; both installers must succeed and leave only cline and ready. Retain the existing installer assertions: ready-runtime reuse performs no download, sequential partial-runtime replacement succeeds, and checksum/version failures publish no replacement. These are proposed checks, not executed results.
There was a problem hiding this comment.
Fixed in 01e0bbb: the readiness re-check and the replacement now run under flock on <runtime>.lock when the image has flock (as Copilot's installer does), so a runtime another installer published meanwhile is kept. test_a_runtime_another_installer_published_is_not_replaced holds the lock as the other installer, checks that the waiting installer leaves the stale runtime alone, then checks that it keeps the runtime published meanwhile; it fails against the previous script (run on Linux). Without flock the installer keeps its previous best-effort publication.
Two seeds installing into one sandbox could both find a stale runtime and both replace it: the second installer then removed the runtime the first had just published, so the first session could not launch Cline. Take a lock (flock, when the image has it) around the readiness check and the replacement, so a runtime published meanwhile is kept. Test: an installer waiting on the lock keeps the runtime another installer published. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
|
/ok to test 01e0bbb |
There was a problem hiding this comment.
The session contract is complete: seed refusals are final 422s, one idempotent activation, close returns a bundle, no host fallback for an unknown session. A run with no run_result raises, a rollout stop before launch is graded as a stop, and the tests cover an infra fault (test_run_without_a_result_fails_the_activation) and model-side endings (test_activation_status_follows_clines_exit_rule, test_classify_outcome_reads_clines_terminal_error). The gaps are in typing, both inline.
Mergeable once an unanswered model call at the deadline is typed as a transport failure and the model request status is typed, or documented as untyped. Needs changes.
| error_message: str | None = None | ||
| if failure is not None: | ||
| status = "incomplete" if isinstance(failure, asyncio.CancelledError) else "failed" | ||
| elif timed_out: |
There was a problem hiding this comment.
Blocking: elif timed_out: status = "incomplete" applies to every deadline, even when stdout holds no model output. #5654 turns "deadline before any model call returned" into failed with model_transport_error. Here a hung model server is scored as the model's incomplete zero. Add the same split and a test.
| # the project elsewhere), so nothing survives to leak into the next rollout. | ||
| shutil.rmtree(work_dir, ignore_errors=True) | ||
|
|
||
| def classify_outcome(self, native_result: Mapping[str, Any]) -> AgentOutcome: |
There was a problem hiding this comment.
Blocking: classify_outcome types only the context overflow, transport errors and the empty-response case. Every Cline error finish with an HTTP status (400, 429, 5xx) stays unknown, so model_request_error is never produced. Parse the status from the event, or state in the README that these endings are untyped and retried, and test that.
| |---|---| | ||
| | `finishReason: completed` (Cline exits 0) | `completed` | | ||
| | `finishReason: error` | `failed`, with the outcome Cline names: a context overflow is `incomplete` instead, a refused or timed-out connection `model_transport_error`, an empty model response `agent_invalid_output`, anything else `unknown` | | ||
| | `finishReason: aborted`, the model's output-token cap, another limit, or the deadline | `incomplete`; the partial work is verified | |
There was a problem hiding this comment.
Non-blocking: output-token-cap endings are incomplete through _reached_model_limit with no typed kind; consistent with #5654, but say so in the README.
| "gym-rollout": body.episode_id.capture_key[:63], | ||
| } | ||
| # Provider TTLs outlive this server: cover installation and the run, plus ten minutes for close. | ||
| settings.setdefault("ttl_s", self.config.sandbox_install_timeout_seconds + self.config.timeout + 600) |
There was a problem hiding this comment.
Non-blocking: owned sandboxes get a TTL but no network_access / restrict_sandbox_network.
giuliolovisotto
left a comment
There was a problem hiding this comment.
Reviewed failure reporting at this head. One non-blocking evidence-retention follow-up below. Harness conformance gaps are not a merge requirement for this review. Source inspection only; no new tests or native rollout were run.
— @codex
| if isinstance(failure, TimeoutError) and timed_out and artifacts is not None: | ||
| # The provider stopped waiting just as the supervisor stopped Cline at its deadline: still a timeout. | ||
| LOG.warning("The sandbox provider stopped waiting for Cline's supervisor after Cline's deadline") | ||
| failure = None |
There was a problem hiding this comment.
Non-blocking: retain the provider wait failure separately from the confirmed harness timeout. This branch clears a real TimeoutError, while the saved SandboxObservation only contains the supervisor timeout and exit status. The warning does not retain the original exception details in the rollout. Keep the current incomplete/gradable result, but also preserve the distinct provider error in saved diagnostics (and its original failure event when available). Extend test_provider_timeout_after_a_confirmed_deadline_is_still_a_timeout to assert both facts survive, rather than only the response status.
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
What does this PR do?
Gives
cline_agentthe agent-session lifecycle (seed, Responses activation behind the session cookie, close) that the merged sandboxed harnesses have, so Cline runs inside the benchmark's task sandbox under asingle_agent_turnEnvironment Server.@cline/cli-linux-{x64,arm64}. At seed,install_cline_runtime.shdownloads that npm tarball inside the sandbox, checks it against a SHA-512 pinned in the adapter (CLINE_RUNTIME_SHA512insandbox.py, npm's owndist.integrity), and publishes only the binary in/tmp/nemo-gym-cline-runtime-<version>with one rename; later sessions in the same sandbox reuse it. Publication is serialized with a 60-second-boundedflock; withoutflock, cold installation/reuse work but stale-runtime replacement fails safely without deleting another session's runtime.directsandbox, or starts an agent-owned one whensandbox_provideris set. An agent-owned sandbox gets a defaultttl_s(install timeout + run timeout + 10 minutes, unlesssandbox_configsets one), the provider's default metadata, thensandbox_configmetadata, thengym-agent,gym-sessionandgym-rolloutlabels. Seed refuses (HTTP 422,AgentSeedRejectedError) required tool grants, a missing or unpinnedcline_version, a missingmodel_server, and host-only settings (command,env,extra_args,repo_dir,openai_*).x-session-idheader that owns every model attempt. It also turns off telemetry and self-update, then runs Cline inSandboxAccess.workdirunder the shared process supervisor (harness_runner.sh).--config,--data-dirand theCLINE_*state paths point into a private session directory, and tool shells keep the task'sHOME/XDG_*. A developer message counts as the system message, andmetadata: {}is accepted.run_resultonly:finishReason: completedgivescompleted.finishReason: errorgivesfailed, withclassify_outcomereading Cline's terminal error. "Model returned empty response" isagent_invalid_output.incomplete(still verified). A deadline without recorded model output givesfailed/unknownwith a timeout marker, so it is retried by default rather than scored as an empty model zero. No model transport cause is invented.doneevent without one, fails the activation.AgentObservationBundle(invocation, per-tool timing from Cline's event timestamps, sandbox record withsandbox_id). Usage comes from Cline's finalrun_result; cache reads are already insideopenai-compatibleinput counts, so they are reported ascached_tokensand not added again./run, sessionless/v1/responses,harness_agent,anyswe_agent) is unchanged, except that Cline now installs on the first host call (shared, retried after a failure) instead of at server startup.configs/cline_agent.yamlbecomes the session config (concurrency: 32); the host config moves toconfigs/cline_local_agent.yamlwith the samecline_agentkey (environments/cline_mathupdated).cline_agentjoinsAGENT_SESSION_AGENT_TYPES, and the adapter'spyproject.tomldepends onnemo-gym[dev,sandbox](like Kilo and Pi), with itsuv.lockregenerated by uv 0.13.4.add-or-update-a-harness.mdxand the Sandboxed harnesses table ofintegrate-existing-agents.mdx.Validation
Current review corrections at
cb1d35f5e(November 6)unknownwith no invented status and default retry. Output-cap endings remainincompletewithout a typed failure. README and regressions state those limits.Checks:
python -m pytest responses_api_agents/cline_agent/tests -q -o addopts='': 148 passed on Linux; 140 passed, 8 Linux-only skips on macOS.git diff --checkand DCO sign-off passed.regex-logsmoke, Resources-owned Docker sandbox, 300-second harness budget: the N35 attempt returned nativeToo Many Requestsand remained an ungraded, retry-eligible failure. A second attempt with NIMminimaxai/minimax-m3.1-flashmade a successful model response and two completed tool calls, then reached the deadline. Its partial response stayedincomplete, verification completed unmasked with reward 0.0, strict--require-sandboxevidence and profiling passed. Health was 0 flagged, 1 unobserved, because the missing final run result leaves aggregate usage unknown. Both attempts' sandboxes and Gym listeners were removed.Follow-up smoke at
9c2253186(November 6)git diff --check, and DCO sign-off passed.regex-log, Resources-owned Docker sandbox, NIMnvidia/nemotron-3.5-super-vl-120b-a12b, 900-second agent budget, one repeat/concurrency 1. Response completed, verifier reward 1.0, unmasked,evaluation_completed: true; 267.82 seconds end to end. All 8 model calls succeeded. Seven tool calls include two missing-Python failures followed by package installation and recovery. Response usage exactly matches capture totals: 167,504 input, 17,898 output, 185,402 total tokens (138,752 cached input).--require-sandboxevidence and profiling passed. Native runtime exited 0; task containers and reserved Gym listeners were empty after cleanup.cb1d35f5e, with the same task/sampling and a 900-second agent budget, still timed out. Its first two calls succeeded in 107.66 and 223.85 seconds; a model call inside Cline'sspawn_agentremained pending beyond 352 seconds and did not return before the supervisor deadline. Its reward was 0.0 because the answer file was absent. Deadline/partial-tool observations, verifier execution, strict sandbox evidence, profiling, and cleanup behaved as expected; this is not a successful solve. The observations locate an outstanding model call but do not distinguish upstream queueing, generation, or network waiting.At
0fb78fd32(review fixes inf2e0ec180and a docstring fix, on top of98ed91450)On macOS. Each fix has a regression test, and each of those tests fails with its fix reverted.
responses_api_agents/cline_agent/tests, in the adapter's locked venv: 131 passed, 5 skipped (the installer tests run only on Linux).tests/unit_tests/test_session_contract_repo.py: 2 passed.tests/unit_tests/tooling/test_add_legacy_agent_environment_servers.py: 122 passed.tests/unit_tests/test_fern_docs_links.py: 29 passed.pre-commit run --files <the 8 changed files>: passed./proc. Neither it nor the rollout-stop tests have run on Linux yet.At
98ed91450(after merging currentmain, which brings in the locked component layout from #4837)macOS with uv 0.13.4 on
PATH:gym env test +entrypoint=responses_api_agents/cline_agent(fresh locked venv): 114 passed, 5 skipped (the installer tests run only on Linux).tests/unit_tests/test_install_cline_runtime.pyon Linux (a workstation, before the merge; the script is unchanged by it): 5 passed.uv run --no-project --python 3.13.14 python scripts/component_locks.py: 269 projects checked, 0 failed.uv lock --project responses_api_agents/cline_agent --checkpasses.pytest tests/unit_tests/test_session_contract_repo.py tests/unit_tests/config/test_global_config.py: 231 passed.pytest tests/unit_tests/tooling/test_add_legacy_agent_environment_servers.py: 122 passed.pytest tests/unit_tests/cli/test_locked_env.py tests/unit_tests/cli/test_component_compatibility.py tests/unit_tests/cli/test_validate_server_configs.py: 50 passed.pytest responses_api_agents/harness_agent/tests: 57 passed.pytest responses_api_agents/anyswe_agent/tests -k cline: 3 passed.pre-commit run --files <the 15 changed files>: passed. It was scoped to these files so auto-fix hooks do not touch unrelated files.x-session-id, mapping the deadline tofailed, accepting required grants, and ignoring failed tool entries each fail a test.gym env resolveforenvironments/cline_math/config.yamlwith the renamed host config.Rollouts
Both smokes ran before the merges of
mainand before the review fixes inf2e0ec180. The merges changed only the adapter's dependency files underresponses_api_agents/cline_agent(thepyproject.toml/uv.locklayout). The review fixes change rollout-stop grading, the owned sandbox's TTL and labels, and how the activation status is read. The current-source November 6 smoke is recorded above; the following results remain historical.1. Borrowed task sandbox, Terminal-Bench 2.1, Docker on a workstation (
smoke-docker-05, commit74a747a59)provider 'enroot' does not support serialize()/connect(), and OpenSandbox pods cannot reach the cluster nodes.minimaxai/minimax-m3.1-flashon NVIDIA NIM through the Gym vLLM Model Server, withreasoning_field: reasoning_contentandmax_tokens: 16384. Earlier attempts onnvidia/nemotron-3-ultra-550b-a55blost every row to NIM 429s (a shared key under heavy use), andz-ai/glm-5.3-flashstalled on NIM 504s.single_agent_turn,resources_tool_transports: [], agenttimeout: 900,--concurrency 1,--allow-unsupported-pairing. TB2.1 tasks at7131e437.incomplete(900 s deadline)completedcompletedcompletedincomplete(900 s deadline)evaluation_completed: trueandharness_execution: sandbox.inspect_harness_conformance.py --require-sandboxfulfills TE-1 to TE-9.ad61a8c88: the same tasks gave rewards 0, 1, 1, 1, 0, but TE-5 failed on the tool call cut off by the deadline, and one deadline row showed a token-count mismatch from Cline's lagging running total. Both are fixed in74a747a59.2. Agent-owned Enroot sandbox,
cline_math(math_with_judge), Slurm cluster (commitad61a8c88)single_agent_turn_legacy, NIMnvidia/nemotron-3-ultra-550b-a55b.completed, 3 to 6 tool calls each. Two rows failed on NIM 429 after Cline's retries; they were reported as retryableunknownoutcomes and not graded as zeros.evaluation_completed, a field math_with_judge does not emit.74a747a59, and the owned-sandbox TTL and labels inf2e0ec180.Compatibility and benchmark impact
configs/cline_agent.yamlis now the agent-session config (resources_server: null,num_workers: 1,concurrency: 32). Configs that imported it for the host/runpath must importconfigs/cline_local_agent.yamlinstead; it keeps the samecline_agentkey and values.cline_agentis now inAGENT_SESSION_AGENT_TYPES, so an unboundcline_agentcomposes with a session Environment Server rather than a legacy relay.allowed_agents; that needs the full TB2.1 evaluation and the benchmark owner's review.Known limits
registry.npmjs.org.curl/tar/gzip/sha512sum.cline_versionneeds its tarball checksums added toCLINE_RUNTIME_SHA512.refs/cline/checkpoints/. Cline's checkpoints leave the working tree and branches untouched.--timeoutdoes not bound a--jsonrun; the process supervisor enforces the deadline.The operation timed out., it is classified asmodel_transport_error. Settingtimeoutin Cline's provider settings breaks connections; use the agent timeout for the wall-clock budget.direct_httpgrants are not wired into Cline. Required grants are refused and optional ones skipped.reasoning_field: bothmakes some NIM endpoints (MiniMax) reject replayed reasoning; setreasoning_field: reasoning_contentthere.cacheReadTokenstoinputTokens, which double-counts cached tokens with theopenai-compatibleprovider. Left as is in this PR.Checklist
pre-commit run --all-files) (so CI lint/format checks pass). Run on the changed files, not--all-files.git commit -s) (so the DCO check passes).🤖 Generated with Claude Code