Repository navigation
Conversation
The Python SDK passed Files API parts to aiohttp as raw bytes, which emits a ResourceWarning above 1 MiB (fatal under -W error). Send a sized io.BytesIO instead so multi-MiB file parts upload with an exact Content-Length.
Hypervisor.create sent POST /vms/create under the client default of 30 s, while the service answers an image create only once the container is ready, waiting up to CREATE_READY_TIMEOUT (110 s). A pull longer than the default raised client-side and left the VM running unaddressed. - execution.py: add CREATE_READY_SECS = 110 beside GATEWAY_REQUEST_BUDGET_SECS - hypervisor.py: create passes request_timeout=max(default, 110 + 120), sized the way command_deadline sizes exec and run - test_facade.py: an image create outlives a 0.05 s client default and the transport receives the 230 s deadline
Add `Hypervisor.vm(self, *, name: str | None = None, id: str | None = None) -> VM` to the Python SDK so callers that already hold a `Hypervisor` can obtain a `VM` handle on the same connection pool and authentication token without constructing a second `VM(url, token, ...)` session. Selector validation (`name` xor `id`) delegates to `VM._select`, matching the public `VM(url, token, *, name=None, id=None)` constructor and the `VmSelector` parameter on `Hypervisor.vm` in the TypeScript and Rust SDKs.
…SourcePackageConfig Extract qualification and audit schema models (`ModulesConfig`, `KingslandingConfig`, `GreyjoyConfig`, `FunctionalConfig`, `DependencyAuditConfig`, `AuditsConfig`) into `build_system/builder/gate/qualifyschema.py` (re-exporting them from `buildschema.py`) to keep `buildschema.py` under the 300-line module ceiling, and add `SourcePackageConfig` for hand-written source packages that have no specification or codegen check.
Add integrations/inspect-ai (inspect-capsem-sandbox), which registers a `capsem` SandboxEnvironment so Inspect AI tasks can run with `sandbox="capsem"`. It talks to the service only through the Python gateway SDK at this revision (it needs google#286, google#287 and `Hypervisor.vm`, so the released 0.6.3 is not sufficient yet). - vm mode (default): exec and file I/O run directly in a VM created from a profile template. - container mode: a plain image boots directly; a Dockerfile or single-service compose.yaml runs under the guest's Docker. Compose and .env parsing follow Docker Compose 2.40. Files without `services:`, multi-service files, depends_on and env_file raise ValueError; other unknown keys log a warning. This code lives in inspect_capsem/containers/, which does not import inspect_ai. - Lifecycle: the sandbox deletes the `inspect-capsem-*` VMs it creates in sample, task and CLI cleanup and at exit, including VMs whose create hit the service's readiness deadline (HTTP 504). - Gateway discovery: `CAPSEM_GATEWAY_URL`/`CAPSEM_GATEWAY_TOKEN`, else the run dir resolved like the CLI (`CAPSEM_RUN_DIR`, `CAPSEM_HOME/run`, `~/.capsem/run`). - Docs: a user page at `web/docs/src/content/docs/usage/inspect-ai.md`, linked from the package README. - Wire the package into config/gate.toml, the gate builder, the fast-gate and CI workflows, codecov.yml, and Citadel ownership. The gate runs Ruff, ty, and pytest with a 90% branch-coverage floor, and builds the wheel/sdist.
ebursztein
left a comment
There was a problem hiding this comment.
Thanks a lot for this, Pierre. It's a really useful integration, and the core design is sound: everything runs inside the Capsem VM via exec or the guest's own Docker, so we found no sandbox escape. The workflow changes are clean too (no new triggers, pinned actions, only the existing CODECOV_TOKEN). Citadel, the package tests (94 passed, 95% branch coverage), ruff, ty and the OSV scan of uv.lock are all green.
We've merged your three SDK PRs (#286, #287, #290) into our integration batch with your authorship. For this one, here's what we'd like changed before it lands.
Blocking
- 300-line ceiling.
[boundary.scripts].roots(config/gate.toml) listssdk/pythonbut notintegrations/, so the package isn't covered. Six source files are over the limit:sandbox.py(869),dockerfile.py(711),compose.py(592),runtime.py(525),image_cache.py(487) and_controller.py(380). Please addintegrations/inspect-aito the roots and split those files by responsibility.
Should fix
- Host memory bound on downloads.
read_filechecks the size withstatin the guest, then_staged_downloadjoins every part with no byte cap. A guest can grow the file after the check, or point at/dev/zero(size 0, not a directory). Please count bytes while streaming, stop at limit + 1, and require[ -f ]._save_built_image_to_cachereads the whole archive before checking its size, which has the same issue. - Ephemeral by default. Naming each sample VM makes it persistent (
_controller.py), which breaks Capsem's ephemeral-by-default rule and leaves workspaces in~/.capsem/run/persistentafter a crash. We'd rather add an SDK/service way to label or find ephemeral VMs. Happy to do that side. - Cleanup scope.
inspect sandbox cleanup capsem <id>deletes any matching VM, including a user's own named VMs. Please require theinspect-capsem-prefix. - Host environment and paths. Compose interpolation, bare
environment: [KEY]entries and bind volumes read host env vars and host paths into the guest. That matches Docker Compose, but it lets a task file carry host credentials past Capsem's credential broker. Please make this opt-in (an allowlist), or at minimum document it prominently. - SDK floor.
pyproject.tomldeclarescapsem>=0.6.3, but the code uses APIs that are newer than 0.6.5. Please raise it to the first release that has them. - Move shared logic into the SDK. Gateway URL/token discovery, the copied
sanitize_file_pathregex, and parsing error text for VM ids and timeouts should be typed SDK APIs. We can add them. - A VM-backed test in the gate.
test_live.pyonly runs withCAPSEM_LIVE_SELF_CHECK. One acceptance test in the VM lane would make this really safe to depend on.
Nits
- Drop the
X as Xre-exports inbuildschema.pyand import fromqualifyschemadirectly. 8a146e35also addsSourcePackageConfig, which only the next commit uses.- Pass the settings explicitly in
sdkchecks.pyinstead of theisinstanceand fallback branches. - Avoid
assertin production code (sandbox.py).
Scope suggestion. It would review and land faster as three PRs:
- The gate refactor.
- The VM-mode sandbox (controller, sandbox, tools).
- Container mode (compose, dockerfile, image cache, CA trust, runtime; about 2.4k lines).
There's one conflict with current work: ModulesConfig gained a transition: TransitionSettings field, so it needs adding where you moved the class (qualifyschema.py).
Thanks again. This is great to have, and we're glad to help with the SDK pieces.
|
Closing in favor of the rebased |
This PR creates a Python package that registers Capsem as a sandbox provider for Inspect AI, similar to the third-party sandbox plugins at https://inspect.aisi.org.uk/extensions-sandboxes.html. I've been testing the plugin locally on various Inspect tasks. A local package works fine but we can also register an official extension in the future with a small PR to Inspect-AI.
Also, a big part of the PR is about handling Docker compose setups required by many Inspect AI tasks. As far as I understand the SDK doesn't provide this functionality, but it sounds useful so I kept the code self-contained in case we want to migrate it later. I didn't implement multi-machine setups yet since I believe there are further networking changes required, but happy to open a follow up PR once this is clear.
AI summary next:
Inspect AI sandbox provider registered as
capsemthrough theinspect_aientry point. Once it's installed,inspect eval <task> --sandbox capsemruns a task's tool calls in a Capsem VM, with no changes to the task. It only uses the Python gateway SDK.Prerequisites
inspect-sandbox-toolsbundle, go through the Files API in one request.Hypervisor.vm): handles for existing VMs by id, on the hypervisor's transport.pyproject.tomldeclarescapsem>=0.6.3, but the plugin also needs SDK APIs that are onmainand not yet released (OCI create fields,EXEC_TIMEOUT_CEILING_SECS,GATEWAY_REQUEST_BUDGET_SECS,decode_exec_output), plus the three PRs above. The plugin isn't meant to be pip-installable yet; in the repo it resolves the SDK fromsdk/python. Could the SDK fixes ship in the next pip release? I'll then bump the floor in a one-line follow-up.What it does
code). The gateway is found like the CLI does:CAPSEM_GATEWAY_URL/CAPSEM_GATEWAY_TOKEN, elseCAPSEM_RUN_DIR,CAPSEM_HOME/runor~/.capsem/run.Dockerfileor a single-servicecompose.yamlgets that container inside the VM.Hypervisor.create(image=...)..envparsing follow Docker Compose 2.40. Files withoutservices:, multi-service files,depends_onandenv_fileraiseValueError; other unknown service keys log a warning.inspect_capsem/containers/and doesn't importinspect_ai(a test enforces this), so it could later move into the SDK or be replaced by native service support.inspect-capsem-*and deletes them insample_cleanup,task_cleanup,cli_cleanup(inspect sandbox cleanup capsem, which only touchesinspect-capsem-*VMs, or the id you pass) and at exit. This includes VMs whose create hit the service's readiness deadline (HTTP 504). If a stop fails, the VM stays tracked and later cleanup retries it.--no-sandbox-cleanupprints the ids of surviving VMs.inspect-sandbox-toolsin the guest and installs the Capsem MITM CA so that TLS clients and Chromium (NSS) trust it.web/docs/src/content/docs/usage/inspect-ai.md(modes, config, env vars, how the image cache and CA trust work). The README links the in-repo file because the docs site is still a holding build. That link adds one record to Citadel'sweb_benchmark_boundary_debt.toml(327 -> 328): itsdocs/pattern also matches the newweb/docs/root.Commits
refactor(gate): moves the qualification and audit schema models frombuildschema.pytoqualifyschema.py, keeping re-exports, so the module stays under the 300-line ceiling. It also addsSourcePackageConfigfor hand-written packages that have no specification or codegen check.feat(inspect-ai): the package, its tests, README, docs page and CHANGELOG entry, plus its gate lane (Ruff, ty, pytest with a 90% branch-coverage floor, wheel/sdist build) inconfig/gate.toml, the fast-gate and CI workflows,codecov.yml, and Citadel ownership.Validation
integrations/inspect-ai:pytest -W error-> 94 passed, 2 skipped (the skipped tests are the live checks), 95.3% branch coverage;ruff check,ruff format --checkandty checkare clean;uv lock --checkpasses.Live, with
CAPSEM_LIVE_SELF_CHECK=1against a running service, in vm and container modes: the full suite (96 tests, including Inspect'sself_check) passes under-W error, and no VMs are left behind.Repository Citadel and gate suite at each commit: 5,694 passed / 46 skipped (commit 1), 5,701 passed / 46 skipped (commit 2).
Unmodified
inspect_evals/inspect_harbortasks with--sandbox capsemandgoogle/gemini-3.5-flash, run one at a time on one host. Every sample completed with 0 sample errors and no VMs were left behind; scores reflect the model, not the sandbox:vimgolf_single_turn --limit 5(Dockerfile)gdm_intercode_ctf --limit 5(Compose + Dockerfile, uncached builds)swe_bench_verified_mini, 3 samples,--message-limit 100terminal_bench_2(harbor), 3 samplesA two-service Compose task fails at sandbox init with the multi-service
ValueErrorand creates no VMs.