Architecture: How to decouple ComputerUseLoopService from E2BDesktopServic...

Documentation for Sessions

πŸ—οΈ Architecture: How to decouple ComputerUseLoopService from E2BDesktopServic...

Date: 2026-02-26
Time: 16:53:19 UTC
Session: gspace1-144.6
Agent: backend
Confidence: 95%
Status: βœ… Documented


πŸ“‹ Decision Context

Question:

How to decouple ComputerUseLoopService from E2BDesktopService to support multiple sandbox backends (E2B Cloud, Self-Hosted Firecracker, Playwright)?

Task: Implement self-hosted E2B provider abstraction (Gap 10)


🎯 Decision Made

Chosen:

Strategy pattern with SandboxProvider interface, SandboxProviderFactory (env-based selection at OnModuleInit), and 3 concrete providers: E2BCloudProvider (wraps existing service), SelfHostedProvider (HTTP to orchestrator), PlaywrightProvider (wraps BrowserAutomationService)

Options Considered:

  1. Adapter pattern wrapping each provider individually β€” rejected: doesn't solve runtime selection
  2. Dependency injection with conditional module imports via useFactory β€” rejected: NestJS doesn't support conditional provider registration cleanly at runtime
  3. Strategy pattern with factory selecting provider at module init β€” chosen: clean separation, env-var driven, testable, backward compatible

πŸ’­ Reasoning

ROOT CAUSE: ComputerUseLoopService had 15+ direct calls to this.e2bDesktop.* methods (screenshot, click, type, scroll, etc.). Adding a second provider would require if/else branching throughout the entire observe-think-act loop. SOLUTION: Strategy pattern with SandboxProvider interface defining 15 methods matching the existing E2BDesktopService API surface. SandboxProviderFactory.onModuleInit() checks env vars in priority order: SANDBOX_SERVER_URL → self-hosted (production Firecracker VMs), E2B_API_KEY → E2B cloud (existing behavior), else → Playwright fallback. E2BCloudProvider wraps E2BDesktopService 1:1, maintaining backward compatibility. SandboxHandle abstracts the sandbox identity (sandboxId, sessionId, userId, timestamps) replacing the raw any type. WHAT WOULD MAKE THIS WRONG: If providers need fundamentally different interaction models (e.g., async screenshot vs sync, or batch actions). Currently all 3 share the same synchronous action→result model. EARLY WARNING: If a new provider needs capabilities not in SandboxProvider interface (e.g., GPU access, audio), the interface needs extension. Monitor for methods that return empty strings or no-ops in PlaywrightProvider — these indicate capability gaps.


πŸ“ Files Involved


πŸ”— Related


Auto-generated by MCP Smart Consolidation