# Custom Sandbox Strategy Registration ## Overview CleverAgents supports custom sandbox strategies via the `SandboxStrategyProtocol`. Custom strategies enable domain-specific isolation for specialized resource types that are not covered by the built-in strategies (none, git\_worktree, copy\_on\_write, transaction\_rollback). ## SandboxStrategy Protocol All custom sandbox strategies must implement the 9-method `SandboxStrategyProtocol`: ```python from cleveragents.domain.models.core.sandbox_strategy import ( SandboxStrategyProtocol, SandboxRef, DiffEntry, DiffView, ) from cleveragents.domain.models.core.resource import Resource class MyCustomSandbox: def create(self, plan_id: str, resource: Resource) -> SandboxRef: ... def read(self, ref: SandboxRef, path: str) -> bytes: ... def write(self, ref: SandboxRef, path: str, content: bytes) -> DiffEntry: ... def diff(self, ref: SandboxRef) -> DiffView: ... def commit(self, ref: SandboxRef) -> None: ... def rollback(self, ref: SandboxRef) -> None: ... def checkpoint(self, ref: SandboxRef, checkpoint_id: str) -> None: ... def restore_checkpoint(self, ref: SandboxRef, checkpoint_id: str) -> None: ... def cleanup(self, ref: SandboxRef) -> None: ... ``` ## Supporting Models ### SandboxRef Opaque reference returned by `create()` and passed to all other methods: ```python @dataclass(frozen=True) class SandboxRef: sandbox_id: str plan_id: str resource_id: str created_at: datetime metadata: dict[str, object] = field(default_factory=dict) ``` ### DiffEntry / DiffView `DiffEntry` describes a single file change; `DiffView` aggregates entries: ```python class DiffEntry(BaseModel): path: str # relative file path operation: str # "added", "modified", or "deleted" before: bytes | None after: bytes | None class DiffView(BaseModel): sandbox_id: str entries: list[DiffEntry] summary: str ``` ## Config-Driven Registration Custom strategies are registered via the `SandboxStrategyRegistry`: ```python from cleveragents.infrastructure.sandbox.strategy_registry import ( SandboxStrategyRegistry, ) registry = SandboxStrategyRegistry( allowed_prefixes=("cleveragents.", "mycompany."), ) # Single registration registry.register( "my_strategy", "mycompany.sandbox.custom", "MyCustomSandbox", ) # Batch config-driven registration configs = { "redis_sandbox": { "module": "mycompany.sandbox.redis", "class": "RedisSandbox", }, "s3_sandbox": { "module": "mycompany.sandbox.s3", "class": "S3Sandbox", }, } registry.register_all_from_config(configs) ``` ## Factory Integration The `SandboxFactory` can be initialized with a custom registry: ```python from cleveragents.infrastructure.sandbox.factory import SandboxFactory factory = SandboxFactory(custom_registry=registry) # Check if custom strategy is available if factory.has_custom_strategy("redis_sandbox"): cls = factory.get_custom_strategy_class("redis_sandbox") ``` ## Built-In Strategy Adapter The `BuiltInSandboxStrategyAdapter` wraps existing `Sandbox` implementations to conform to the `SandboxStrategyProtocol`: ```python from cleveragents.infrastructure.sandbox.strategy_adapter import ( BuiltInSandboxStrategyAdapter, ) adapter = BuiltInSandboxStrategyAdapter(strategy_name="copy_on_write") ref = adapter.create("plan-001", resource) adapter.write(ref, "file.txt", b"content") data = adapter.read(ref, "file.txt") adapter.cleanup(ref) ``` ## Protocol Validation All strategies are validated against the Protocol at registration time. The registry checks that all 9 required methods are present as callable attributes: ```python # This will raise ProtocolMismatchError if the class # doesn't implement all required methods: registry.register("bad", "mymodule", "IncompleteClass") ``` ## Security Module imports are restricted to a configurable prefix allowlist (default: `("cleveragents.",)`). Only modules whose fully-qualified name starts with an allowed prefix may be dynamically imported.