# 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.
