feat: pluggable sandbox capability - #364
Conversation
- Introduced a framework-agnostic core interface for executing agent-generated code in isolated environments. - Defined terminology including Sandbox, Provider, Principal, Policy, Capabilities, and Scope. - Outlined goals and non-goals for version 1 of the sandbox capability. - Described architectural placement and core interfaces for sandbox operations. - Established permission boundary using RBAC with detailed principal and policy definitions. - Included factory and registration mechanisms for built-in and user-defined providers. - Specified lifecycle and session binding for sandbox instances.
…orkspaces - Added a new framework-agnostic sandbox capability allowing agents to execute LLM-generated code, work in persistent isolated workspaces, and attach to existing runtimes. - Implemented a dual identity model for permission boundaries, ensuring secure execution under agent or user identities. - Created a new package `ak-py/src/agentkernel/sandbox/` with core interfaces and public APIs for sandbox management. - Defined three usage modes: code-execution tool, sandboxed workspace, and attach to existing runtime. - Established a configuration mechanism for enabling and managing sandbox capabilities, including support for custom backend providers. - Removed the previous sandbox specification document as the new design supersedes it.
There was a problem hiding this comment.
Pull request overview
This PR introduces the initial documentation set for AK-133 (“Sandbox capability”): a stage-1 design.md plus a new developer research skill containing the background research and reference material that informed the design.
Changes:
- Add
docs/specs/ak-133/design.mddefining the proposed sandbox capability requirements and architecture. - Add developer skill
.agents/skills/ak-dev-sandbox-research/documenting scope, conclusions, and how to continue the research. - Add supporting research references (provider landscape, framework prior art, and AK codebase patterns) under the new skill.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/specs/ak-133/design.md | Stage-1 design spec for AK-133 sandbox capability (requirements + proposed architecture). |
| .agents/skills/ak-dev-sandbox-research/SKILL.md | Dev skill describing the research goal, scope, key findings, and linking to the design spec. |
| .agents/skills/ak-dev-sandbox-research/references/provider-landscape.md | Provider landscape survey and capability matrix used to inform design constraints. |
| .agents/skills/ak-dev-sandbox-research/references/framework-abstractions.md | Prior-art survey of executor/sandbox abstractions across major agent frameworks/SDKs. |
| .agents/skills/ak-dev-sandbox-research/references/ak-codebase-patterns.md | Summary of existing AK pluggable-capability patterns relevant to implementing sandboxing. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
amithad
left a comment
There was a problem hiding this comment.
Spec-stage review — Stage 1 (design.md + research skill). No implementation code, so dimension 6 (spec conformance) is skipped.
Overall: a strong, correctly staged design PR. Findings are consistency gaps and skill-sync housekeeping, nothing structural.
design.mdverdict: point-form, hierarchical, complete for design altitude, exactly one diagram, and everypath:lineclaim verified against currentdevelop— the greenfield grep,guardrail/guardrail.py:25-68,core/multimodal/storage/storage_manager.py:33-84,core/tool.py:165-179,core/runtime.py:12,core/config.py:375-405,pyproject.toml:23,examples/cli/smolagents/demo_codeagent.py:27all check out. Open questions record dated resolutions instead of silent decisions, andspec.mdis properly deferred until this design review settles.- Inline findings: 2
[question](session-end teardown trigger doesn't exist in core today; concurrency-contract scope vsper_runtime/suspend-resume), 3[suggestion](SandboxManagerreferenced but never defined; the skill's Open Design Questions are resolved bydesign.mdin this same PR; stalecore/config.pyline citations inak-codebase-patterns.md), 1[nit]. - Un-anchorable — skill inventory docs not updated:
docs/docs/agent-skills.md:116says "fourteen developer skills" (table at 122–135) anddocs/specs/agent-skills.md(table at 58–71 + directory tree) enumerate every dev skill; this PR makes it fifteen but touches neither surface (ak-dev-sync-skills-from-branchStep 6 / validation #4). Ifak-dev-sandbox-researchis deliberately excluded as a transient research skill, note that somewhere; otherwise add the rows and bump the count. - Un-anchorable — PR hygiene per
ak-dev-write-spec: a spec/skill-only PR should use adocs:title (current: "Feature/sandbox capability"), fill the PR template marking Documentation update, and state that implementation follows in a separate PR — the body is currently empty. The branch'sfeat:commit messages ("feat: introduce pluggable sandbox capability…") also read as shipped code, which this PR doesn't contain. - Scope note: external-source claims in
provider-landscape.md/framework-abstractions.md(provider capabilities, SDK shapes, pricing, GitHub-issue pain points) were not independently verified; the AK-codebase claims in all three research documents were, and apart from the flaggedconfig.py/runtime.pydrift they verify.
…ons and resolved questions
…nd broker flavors - Introduced the `agentkernel/sandbox/` package containing the `Sandbox` and `SandboxProvider` interfaces. - Added support for sandbox sessions, RBAC permission boundaries, and a queue-decoupled sandbox broker. - Implemented seven first-party providers: local_subprocess, docker, e2b, daytona, bedrock_agentcore, kubernetes, and ec2_ssm. - Established a comprehensive configuration model for sandbox capabilities, including identity and policy configurations. - Enhanced error handling with a dedicated hierarchy for sandbox-related errors. - Developed system tools for executing code, commands, and file operations within sandboxes. - Created a testing suite for sandbox functionality, including mock providers and end-to-end tests for each provider. - Documented the new capability, including usage examples and provisioning details for AWS deployments.
amithad
left a comment
There was a problem hiding this comment.
Re-review — the PR now carries design.md (stage 1) and a new spec.md (stage 2). Still no implementation code, so dimension 6 (spec conformance vs code) remains skipped.
Overall: the revision quality is high — every finding from the previous review round was addressed substantively (teardown now anchored on a mandatory idle timeout instead of a nonexistent session-end event; the concurrency contract scoped per path; SandboxManager defined; the research skill's questions marked resolved; citations refreshed with symbol-name anchoring), and the PR title/body now follow the docs: spec-PR guidance. The new spec.md is thorough and its factual claims verify.
spec.mdclaims verified against the codebase:BaseRunRequestextra="allow"(core/model.py:226),RequestBuilder._attach_additional_context→AgentRequestAny(core/chat_service.py:117-130—core/runtime.py's docstring even states AgentRequestAny is pre-hook-only),core/runtime.py:49/198,core/tool.py:144-151/165-179/181-197,akagentrunner.py:90,deployment/aws/core/response_store/{redis,valkey,dynamodb}.py,_ResponseStoreConfig, the pickle-based serde, andtests/test_runtime.py:19-22; I independently re-ran the "no test asserts the pre-hook list composition" grep — it holds. TheAgentRequestAnydeviation is well-founded and properly flagged in both documents — exactly the process the spec flow asks for.- Design-requirements walk: nearly everything in
design.mdtraces to a spec section (interface, capabilities/isolation tiers, RBAC fail-closed paths, sandbox sessions/self-heal/idle teardown, DB-first completions, dedup, config, factory/BYO, all 7 providers, testing, docs deliverables). The inline findings are the residue: 2 [question] (stage-2 spec shipping while the stage-1 review is still open; broker flavor set deviating from design.md without a callout), 4 [suggestion] (client-side Lambda fail-fast has no worker-ceiling knowledge; the idle sweep reads response-DB session records nothing writes; 3-vs-5 tool count;per_runtimepooling deferred to spec.md but unanswered), 2 [nit]. - Skipped as already raised: the skill-inventory mismatch (PR body claims
docs/docs/agent-skills.md+docs/specs/agent-skills.mdregister the skill, but neither file is in the diff) — flagged by Copilot on this PR; and thefeat:-typed commit messages describing unshipped code — raised in the previous review round. - CI: all checks passing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…r sandbox capability
amithad
left a comment
There was a problem hiding this comment.
Round-3 review — the spec set is now complete: design.md (approved), spec.md (approved), and the new stage-3 plan.md. Still no implementation code, so dimension 6 (spec conformance vs code) remains skipped.
Overall: this round is nearly clean. All 8 findings from the previous review were fixed substantively and verifiably in bea1fec4 — the fail-fast gap became a worker_timeout_ceiling terraform output with client-side SandboxPolicyError rejection; the idle sweep gained a fully specified broker-side session inventory (session:<id> records, per-op last_used_at refresh, max(2×idle_timeout, response_ttl) TTL, per-backend scan); the broker-flavor consolidation was folded back into design.md with the dated-deviation pattern; per_runtime pooling is explicitly closed as "none in v1"; tool count, SandboxManager labeling, and the test-file split are all reconciled. Copilot's round-2 comments (staging wording, design altitude, default_factory, skill status, deviation framing) were addressed in the same commit, and the staging question is settled: the PR body now declares the design approved (2026-07-16) and the single-branch flow deliberate.
plan.mdverdict (stage 3, reviewed at plan altitude): template-conformant — every iteration has Goal/Files/Steps/Verify with concrete test commands, steps reference spec sections instead of restating them, and each iteration leaves the branch testable. I mapped spec.md's components across the iterations: all land in exactly one iteration (7/7 providers, broker flavors, wiring points, terraform), and the mandatory Tests (11) and docs/skills-sync (12) iterations are present — iteration 12 is notably complete (inventories, architecture skill, research-skill status flip, verified-no-update surfaces). The iteration-7ec2_ssmevaluation checkpoint before the AWS plane is a sensible risk gate. The "Future requirements → seams" section covers every deferred design non-goal that needs one.- New findings: 1
[suggestion](plan.md introducestests/test_sandbox_tools.py, a fourth test file spec.md's Testing section doesn't define, plus an It-11 wording conflict with the spec about touchingtest_runtime.py), 1[nit](the research skill saysplan.md"follows" — it now ships in this PR). - Skipped as already raised (still open): the skill-inventory registration — the PR body's "Changes Made" still claims
docs/docs/agent-skills.mdanddocs/specs/agent-skills.mdregister the new skill, but neither file is in the diff (Copilot's earlier comment). Note the dependency: plan.md line 149's "fifteen → sixteen" arithmetic assumes this PR already made it fifteen. - CI: green (one check still completing at review time).
…ence Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ker) - Resolve an explicit sandbox_session_id under the profile it was minted with, not the caller's/default; a contradicting profile raises SandboxConfigError instead of silently rerouting to another provider. - Persist the completed run's session handle on wait-expiry promotion so the next operation attaches instead of orphaning the sandbox; carry the idle-reset notice on the promoted SandboxTask (and surface it in the tool JSON). - Route completion-routing `agent` from the agent name, never principal.subject (which is the end-user id under user identity). - Prune in-process broker completions once consumed via a new discard() primitive (thread/embedded), bounding the REST server's footprint. - Fail loud on an unknown non-dotted broker flavor with the #541 error shape. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…iterations The providers land in post-merge iterations 9-10; declaring the extras now ships installable SDKs no code imports. Keep only the sandbox-docker extra (its provider is implemented). Regenerate the lock. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Regression tests for the two blockers (explicit-session profile routing, wait-expiry promotion) plus warn-once, per_call finally-on-failure, real Runtime.run round-trip, promoted-task tool JSON, non-files capability error, broker unknown-flavor, stale-handle same-id, and require_extra wiring for trace/guardrail built-ins. - Close the leaked thread-broker daemon in the broker-test fixture teardown. - Fix the openai-dynamic e2e expectation: a failed !select is a no-op (logged to stderr, which the Test harness no longer captures), so assert the observable outcome instead of the log message. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…with review - spec.md: Consumer/Behavioural changes for test.py stderr split, `agents` scoping, and core/base.py; rule 2 (worker AKConfig), rule 6 (testing not re-exported), tool-guidance capabilities wording, provisioning-error deviation, atexit deferral. - 494 design: eight tools + `agents` key; 541 design: exclude the AWS response handler from the fail-loud bullet, note BYO contracts live in factory docstrings. - plan: correct the nonexistent inventory-file reference; extras + atexit deferrals. - docs site: identity example uses a BYO provider (kubernetes not shipped), qualify wait_timeout to the thread flavor, correct the tool-echo claim and ABC contract. - examples: mark unshipped providers "(planned)"; demo provider declares attach=True; fix the basic README's identity path. - ak-dev-new-sandbox-provider / ak-dev-architecture: ABC-contract wording; CJK typo. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Address Copilot review on examples/api/sandbox-identity: - execute_code raises SandboxCapabilityError for an undeclared language (a typo like "pyhton" no longer silently runs under bash). - _run kills and reaps the subprocess on timeout, raising SandboxTimeoutError, so a timed-out execution can't leak an orphaned process or hang shutdown. - app_test server-teardown gets a bounded wait + kill() fallback. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
amithad
left a comment
There was a problem hiding this comment.
Round-5 re-review — verification of the round-4 findings (head 33a92a1e, five fix commits).
Overall: every round-4 finding is resolved, and the fixes were verified against the code — not just the replies. One residual suggestion (inline) on the completion-retention fix; nothing else found in the fix delta.
- Blockers (3/3 verified fixed):
- Session/profile routing: re-ran the original repro against
33a92a1e— an explicitsandbox_session_idnow resolves under the profile it was minted with (_resolve_profile_name), the gpu-profile session executes on the gpu provider, and a contradicting callerprofileraisesSandboxConfigError. The regression testtest_manager_explicit_session_uses_its_own_profilelocks it in. - Promotion handle loss: same repro — the promoted run's
sandbox_idnow lands in the registry viatask_status→_write_session(completion.sandbox_session), and the second call reattaches instead of orphaning. The idle-resetnoticenow ridesSandboxTasktoo (model + tool JSON), closing the "recreation is never silent" gap on the promotion path. - stderr e2e:
run-tests / e2e-tests (1, cli, examples/cli/openai-dynamic)passes — the test now asserts the observable no-op behavior of a failed!select, and thetest/test.pychange is documented in spec Consumer + Behavioural changes. Keeping the stderr split is a reasonable call.
- Session/profile routing: re-ran the original repro against
- Suggestions/questions/nits: all 32 addressed — 27 fixed as claimed (spot-verified: the seven new/strengthened tests exist and assert what was promised; broker fail-loud flavor error matches the #541 shape;
agentrouting uses_current_agent_name(); docs/skills/examples corrections all present, including the CJK fix and the identity example'sattach=True), 3 resolved by documented deviation (spec rule 2 now listsbroker/worker.py;SandboxProvisionErrorwrapping and the ABC-contract docs recorded as explicit follow-ups in spec/#541 design), 2 deferred with proper dated plan entries (theatexitbackstop → new post-merge iteration 8.5 with spec §Error-handling note; thee2b/daytona/kubernetesextras → iterations 9-10, removed from pyproject). The evalsmust_not_containanswer (local guidance never emitssandbox-docker) is accepted. - Tests: full suite at head — 190 passed (was 182; the eight additions match the claimed tests). Both prior repro scripts now print the fixed behavior.
- CI: fully green — 0 pending, 0 failing at review time.
- PR hygiene: title is now
feat: pluggable sandbox capabilityand the body accurately describes the iterations 1-6 scope with a dated correction note — the long-standing docs-only staleness (raised by Copilot and carried through earlier rounds) is closed. - Fix-delta review: the new code introduced by the five commits was reviewed as a delta (manager
_resolve_profile_name/_current_agent_name,SandboxBroker.discard, factory flavor fail-loud,SandboxTask.notice, spec/plan/design syncs, identity-example hardening) — apart from the inline residual, no new issues.
With the inline follow-up noted, this is ready for maintainer sign-off from my side.
…ocs) - Bound the in-process brokers' completion retention with an LRU-capped store so a long-running server on the synchronous path no longer accumulates a completion per execution for the process lifetime (thread + embedded); discard() still frees eagerly. - LangGraphToolBuilder.bind() accepts an optional agent_name and passes it to SystemToolFactory.get_all() so per-capability `agents` scoping is honored. - Docker provider file ops reject `..` traversal escaping /workspace (absolute paths are neutralized to workdir-relative, mirroring local_subprocess); regression test added. - Sandbox broker `flavor` config description lists only the implemented flavors and notes `sqs` is planned. - Identity demo provider requires a config arg (follows the real BYO SandboxProvider contract). - Fix the policy example's garbled pyproject description. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The four sandbox examples had build.sh + test files but weren't in the e2e
matrix (.github/test-config.yaml). Add cli/sandbox/{basic,profiles,policy} and
api/sandbox-identity so CI exercises them; all use local_subprocess (no Docker
daemon) and the workflow-provided OPENAI_API_KEY.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Group `pydantic` with the other third-party imports (after `agentkernel.*`, alphabetical) per the examples' isort profile — resolves the lint-examples-check failure introduced when the BaseModel import was added. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Description
Pluggable sandbox capability for Agent Kernel (#494, formerly AK-133): agents execute code and shell commands in an isolated, permission-bounded environment, with pluggable providers, workload profiles, policy enforcement, and per-user identity. This PR ships the spec set and the first implementation slice (iterations 1–6) — working
local_subprocess/dockersandboxes on CLI and REST — plus the shared pluggable-backend factory refactor (#541).Type of Change
Related Issues
Closes part of #494 (iterations 1–6); relates to #541.
Changes Made
Sandbox capability (
ak-py/src/agentkernel/sandbox/)Sandbox/SandboxProviderABCs with honest per-providerSandboxCapabilities;SandboxErrorhierarchySandboxManager(profile routing, sessions with nv_cache registry, fail-closed principal/policy enforcement, idle-reset notices)SandboxProviderFactory/SandboxBrokerFactory;embedded+threadbroker flavors withBrokerWorkerCoreSandboxPreHooktask-completion ingestion; self-describing via injected system prompt, optionally scoped viasandbox.agentslocal_subprocess(no isolation; dev/test) anddocker(container);FakeSandboxProvider+ reusableSandboxProviderContractShared factory pattern (#541)
core/util/factory.py(resolve_dotted,require_extra,AKConfigError); trace, guardrail, session/thread/multimodal-store, and sandbox factories converge on theif/elifreal-import + dotted-path-BYO shape; unknowntypenow fails loudCore wiring (three points)
core/config.py(sandboxsection),core/tool.py(SystemToolFactory, per-agent scoping),core/runtime.py(third system pre-hook);core/base.pypassesagent.nameto the factoryExamples & docs
examples/cli/sandbox/{basic,profiles,policy}andexamples/api/sandbox-identity(end-to-end per-user identity over REST)docs/docs/advanced/sandbox.md), spec/design/plan underdocs/specs/494-sandbox-capability/, newak-dev-new-sandbox-providerskillBehavior changes
typeacross the pluggable factories (previously some silently fell back to in-memory)Testframework keeps CLI stderr on a separate pipe (logs no longer pollute captured responses)Testing
cd ak-py && uv run pytest— 851 passed; the only failures are 3 pre-existingtest_cli_testerlive-LLM tests unrelated to this branch)test_sandbox.py,test_sandbox_broker.py,test_sandbox_providers.py,test_factory.py,test_store_builders.py,test_trace.py, plus example tests)Checklist
Additional Notes
Iterations 1–6 land here; iterations 7–12 (remaining providers, AWS broker plane,
atexitbackstop, final docs pass) are sequenced post-merge indocs/specs/494-sandbox-capability/plan.md. Design decisions and dates are recorded indesign.md.