Skip to content

feat(agent-sdk): per-session sandbox workspaces (Pattern C)#6

Merged
cognect merged 1 commit into
mainfrom
feat/agent-sdk-sandbox-workspace
Jun 8, 2026
Merged

feat(agent-sdk): per-session sandbox workspaces (Pattern C)#6
cognect merged 1 commit into
mainfrom
feat/agent-sdk-sandbox-workspace

Conversation

@cognect

@cognect cognect commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Pattern C — per-session sandbox workspaces for the Agent SDK harness

Contains the harness's bypassPermissions default for untrusted work: each
interactive session (and each headless run) can get its own isolated microVM
sandbox
as a disposable workspace. Feature-gated (agent-sdk-harness/sandbox)
and off unless spec.workspace.enabled.

What it does

  • Stages the .claude/ projection into the sandbox (write_file) instead
    of the host filesystem — necessary because a SandboxHandle has no
    host-visible mount, so the host claude CLI can't cd into it.
  • Routes the agent's work into the sandbox: injects an in-process
    run_in_sandbox tool bound to the session's sandbox and disables host
    Bash/Write/Edit
    (true containment).
  • Discards (default) or snapshots the workspace to the warm pool on
    close; warm starts fork from the SnapshotPool.

The shared-registry seam (no Arc serialized across PyO3)

One SandboxClient is passed to the harness; the Rust side holds the same
Arc<SandboxHarness>, and the Python run_in_sandbox tool closes over the same
client — so the per-session sandbox the harness creates is the exact one the
tool execs into. The session's SandboxId rides the normal config round-trip
(config.sandbox_workspace_id); SandboxClient.attach(id) binds the tool.

Changes

  • agent-sdk-core: AgentSdkConfig.sandbox_workspace_id (plain, optional data).
  • agent-sdk-harness: new sandbox feature; workspace module
    (SandboxWorkspaceConfig / WorkspaceDisposition / SessionWorkspace /
    WorkspaceRegistry); render_projection refactor (pure renderer reused by
    the host-fs materialize and the new stage_into_sandbox sink);
    acquire_workspace / release_workspace / apply_containment wired into
    run / start_session / stop_session.
  • py-bindings: SandboxClient.attach() + harness_arc();
    from_python_backend(sandbox=…); harness built with features=["sandbox"].
  • python: _make_run_in_sandbox factory; sandbox= plumbed through
    ClaudeAgentSDKBackend / _build_options / agent_sdk_backend / harness.
  • umbrella: agent-sdk-sandbox forwarder feature.
  • docs / CHANGELOG / README: Pattern C section + cross-links.

Tests

  • cargo test -p atomr-agents-agent-sdk-harness --features sandbox19 pass
    (11 new, MockBackend, network-free): acquire+stage, exec routing, containment
    flags, discard, snapshot, warm reuse, disabled path, enabled-no-client, two
    quotas, headless discard, projection parity.
  • cargo test -p atomr-agents-agent-sdk-harness9 pass (base parity, no
    feature compiled).
  • cargo build --workspace, cargo check -p atomr-agents-py-bindings,
    cargo build -p atomr-agents --features agent-sdk-sandbox — all build.
  • Python smoke (mock backend) verifies the shared-registry attach + exec path
    and harness construction with a workspace-enabled spec.

Pattern B (whole claude CLI inside the VM via a SandboxAgentSdkBackend) and
a host-side credential proxy are noted as future roadmap.

🤖 Generated with Claude Code

Each interactive session and headless run can get its own isolated microVM
sandbox as a disposable workspace, behind the agent-sdk-harness `sandbox`
feature. The .claude/ projection is staged into the sandbox, the agent's
exec/file is routed there via an in-process `run_in_sandbox` tool (host
Bash/Write/Edit disabled = true containment), and the workspace is discarded
- or snapshotted to the warm pool - on close. One SandboxClient is shared
between the Rust harness and the Python tool so both resolve the workspace
from a single registry (no Arc serialized across the boundary).

- agent-sdk-core: add AgentSdkConfig.sandbox_workspace_id (plain data)
- agent-sdk-harness: `sandbox` feature; workspace module (config/disposition/
  registry); render_projection refactor + stage_into_sandbox sink;
  acquire/release/apply_containment wired into run/start_session/stop_session
- py-bindings: SandboxClient.attach() + harness_arc(); from_python_backend
  sandbox=; enable harness `sandbox` feature
- python: _make_run_in_sandbox factory + sandbox= plumbing through
  ClaudeAgentSDKBackend/_build_options/agent_sdk_backend/harness
- umbrella: agent-sdk-sandbox forwarder feature; docs + CHANGELOG + README

Tests: 19 harness tests pass with the feature (11 new, MockBackend,
network-free), 9 without (base parity); py-bindings + full workspace +
umbrella forwarder build; Python smoke verifies the shared-registry
attach+exec path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cognect cognect merged commit f15ab3a into main Jun 8, 2026
1 of 5 checks passed
@cognect cognect deleted the feat/agent-sdk-sandbox-workspace branch June 8, 2026 22:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant