diff --git a/docker-compose.yml b/docker-compose.yml index 5bdd390c0..942a3de09 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -158,7 +158,7 @@ services: apt-get update && apt-get install -y --no-install-recommends curl ca-certificates libdigest-sha-perl curl -sSL https://get.microsandbox.dev | sh fi - exec msb server start --dev + exec msb server start --dev --host 0.0.0.0 restart: unless-stopped healthcheck: test: ["CMD", "curl", "-f", "http://localhost:5555/health"] diff --git a/scripts/docker/supervisor-allinone.conf b/scripts/docker/supervisor-allinone.conf index b935737d9..2a0c4fe81 100644 --- a/scripts/docker/supervisor-allinone.conf +++ b/scripts/docker/supervisor-allinone.conf @@ -118,7 +118,7 @@ environment=NODE_ENV="production",PORT="3000",HOSTNAME="0.0.0.0" # Autostart is controlled by the entrypoint based on MICROSANDBOX_ENABLED env var. # Requires --device /dev/kvm and --privileged when running the container. [program:microsandbox] -command=msb server start --dev +command=msb server start --dev --host 0.0.0.0 autostart=%(ENV_MICROSANDBOX_AUTOSTART)s autorestart=true priority=25 diff --git a/surfsense_backend/app/agents/new_chat/sandbox.py b/surfsense_backend/app/agents/new_chat/sandbox.py index 53e71329a..84ba9fac1 100644 --- a/surfsense_backend/app/agents/new_chat/sandbox.py +++ b/surfsense_backend/app/agents/new_chat/sandbox.py @@ -5,13 +5,74 @@ Manages the lifecycle of sandboxed code execution environments. Each conversation thread gets its own isolated sandbox instance. """ +from __future__ import annotations + import logging import os -from deepagents_microsandbox import MicrosandboxBackend, MicrosandboxProvider - logger = logging.getLogger(__name__) +# --------------------------------------------------------------------------- +# Compatibility shim +# --------------------------------------------------------------------------- +# deepagents-microsandbox imports SandboxInfo, SandboxListResponse, and +# SandboxProvider from deepagents.backends.sandbox. These types were added +# in a fork and have not yet landed in the official deepagents package. +# We inject minimal stubs so the import succeeds without patching the venv. +# --------------------------------------------------------------------------- + +def _ensure_sandbox_provider_types() -> None: + """Inject missing SandboxProvider / SandboxInfo types if absent.""" + import importlib + sandbox_mod = importlib.import_module("deepagents.backends.sandbox") + + if hasattr(sandbox_mod, "SandboxProvider"): + return # Already present – nothing to do. + + from abc import ABC, abstractmethod + from dataclasses import dataclass, field + from typing import Any, Generic, TypeVar + + _M = TypeVar("_M") + + @dataclass + class SandboxInfo(Generic[_M]): + sandbox_id: str + metadata: _M = field(default_factory=dict) # type: ignore[assignment] + + @dataclass + class SandboxListResponse(Generic[_M]): + items: list[SandboxInfo[_M]] = field(default_factory=list) + cursor: str | None = None + + class SandboxProvider(ABC, Generic[_M]): + @abstractmethod + def list(self, *, cursor: str | None = None, **kwargs: Any) -> SandboxListResponse[_M]: ... + + @abstractmethod + async def alist(self, *, cursor: str | None = None, **kwargs: Any) -> SandboxListResponse[_M]: ... + + @abstractmethod + def get_or_create(self, *, sandbox_id: str | None = None, **kwargs: Any) -> Any: ... + + @abstractmethod + async def aget_or_create(self, *, sandbox_id: str | None = None, **kwargs: Any) -> Any: ... + + @abstractmethod + def delete(self, *, sandbox_id: str, **kwargs: Any) -> None: ... + + @abstractmethod + async def adelete(self, *, sandbox_id: str, **kwargs: Any) -> None: ... + + sandbox_mod.SandboxInfo = SandboxInfo # type: ignore[attr-defined] + sandbox_mod.SandboxListResponse = SandboxListResponse # type: ignore[attr-defined] + sandbox_mod.SandboxProvider = SandboxProvider # type: ignore[attr-defined] + + +_ensure_sandbox_provider_types() + +from deepagents_microsandbox import MicrosandboxBackend, MicrosandboxProvider # noqa: E402 + _provider: MicrosandboxProvider | None = None diff --git a/surfsense_backend/pyproject.toml b/surfsense_backend/pyproject.toml index 5f79c3154..f9359c9b5 100644 --- a/surfsense_backend/pyproject.toml +++ b/surfsense_backend/pyproject.toml @@ -67,6 +67,7 @@ dependencies = [ "typst>=0.14.0", "deepagents>=0.4.3", "deepagents-microsandbox>=1.0.1", + "microsandbox>=0.1.8", ] [dependency-groups] diff --git a/surfsense_backend/uv.lock b/surfsense_backend/uv.lock index 68e7d1f1b..ef3d0fbe0 100644 --- a/surfsense_backend/uv.lock +++ b/surfsense_backend/uv.lock @@ -6895,6 +6895,7 @@ dependencies = [ { name = "markdown" }, { name = "markdownify" }, { name = "mcp" }, + { name = "microsandbox" }, { name = "notion-client" }, { name = "numpy" }, { name = "pgvector" }, @@ -6967,6 +6968,7 @@ requires-dist = [ { name = "markdown", specifier = ">=3.7" }, { name = "markdownify", specifier = ">=0.14.1" }, { name = "mcp", specifier = ">=1.25.0" }, + { name = "microsandbox", specifier = ">=0.1.8" }, { name = "notion-client", specifier = ">=2.3.0" }, { name = "numpy", specifier = ">=1.24.0" }, { name = "pgvector", specifier = ">=0.3.6" },