Files
temp/docs/development/custom_sandbox_strategy.md
freemo 2688c85769 feat(extensibility): implement Custom Sandbox Strategy Registration via SandboxStrategy Protocol
Implement SandboxStrategyProtocol, a 9-method @runtime_checkable Protocol enabling
third-party sandbox strategy registration. Includes:

- SandboxStrategyProtocol with create/read/write/diff/commit/rollback/checkpoint/
  restore_checkpoint/cleanup methods
- SandboxRef (frozen dataclass) and DiffView/DiffEntry (Pydantic models)
- SandboxStrategyRegistry with config-driven registration, Protocol validation,
  thread safety, and clear/list/has operations
- BuiltInSandboxStrategyAdapter wrapping existing Sandbox implementations to conform
  to the new Protocol
- CustomStrategyConfig for YAML/dict-based strategy registration
- SandboxFactory integration with custom_registry parameter,
  has_custom_strategy() and get_custom_strategy_class()
- 25 Behave BDD scenarios (85 steps) covering protocol, registry, adapter, config,
  and factory integration
- 8 Robot Framework integration tests with real filesystem operations
- ASV benchmarks for registry and adapter operations
- Developer documentation

ISSUES CLOSED: #586
2026-03-16 22:50:27 +00:00

151 lines
4.0 KiB
Markdown

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