-
- Notifications
You must be signed in to change notification settings - Fork 1
Description
Metadata
IMPORTANT: The very first step should ALWAYS be validating this metadata section to maintain a CLEAN development workflow.
pull_request_title: "FROM feat/[issue#]-openshell-sandbox TO development" branch: "feat/[issue#]-openshell-sandbox" worktree_path: "$WORKSPACE/.worktrees/feat-[issue#]"User Stories
- As a platform admin, I want OpenShell as a third sandbox backend option so that DeepAgents can execute tools in NVIDIA OpenShell sandboxes alongside Daytona and State.
- As a user, I want to select OpenShell from the sandbox dropdown in settings so that my agent runs execute in an OpenShell sandbox when I have it configured.
- As a developer, I want the auto-fallback chain to try Daytona → OpenShell → State so that the system gracefully degrades when a preferred sandbox is unavailable.
Summary
Add OpenShell as a third sandbox backend option alongside Daytona and State, following the exact existing provider patterns. The integration is "minimal" — it mirrors the Daytona integration structure: a conditional import guard, a factory function, registration in _SANDBOX_FACTORIES, extension of the auto-fallback chain, and exposure through the existing user settings and frontend sandbox selector. No new APIs, no new database columns, no new routes — just a new backend choice threaded through existing wiring.
Fallback chain (auto mode): Daytona → OpenShell → State
Visual Reference
- See
specs/spec-a-minimal-backend.mdfor full architecture details and code samples.
Key Integration Points
| File | Function(s) | Role |
|---|---|---|
backend/pyproject.toml | dependencies | Add openshell>=0.0.10 dependency |
backend/src/constants/__init__.py | UserTokenKey, env vars | Add OPENSHELL_API_KEY, OPENSHELL_GATEWAY, OPENSHELL_SANDBOX_NAME |
backend/src/agents/__init__.py | create_openshell_backend(), resolve_sandbox_backend() | Conditional import, factory, updated fallback chain |
backend/src/agents/openshell.py | OpenShellBackend | New file — BaseSandbox implementation wrapping OpenShell sessions |
backend/src/schemas/entities/settings.py | SandboxType | Add OPENSHELL enum value |
backend/src/controllers/llm.py | llm_invoke() | OpenShell error handling (mirrors Daytona pattern) |
backend/src/utils/stream.py | stream_generator() | OpenShell error handling (mirrors Daytona pattern) |
backend/src/workers/tasks.py | _execute_agent_stream() | OpenShell error handling (mirrors Daytona pattern) |
UI Integration Points
| Component / Route | Change Type | Description |
|---|---|---|
frontend/src/lib/services/userSettingsService.ts | Modify | Add "openshell" to SandboxType union |
frontend/src/lib/config/sandbox.ts | Modify | Add OpenShell to SANDBOX_OPTIONS, update normalizeSandboxValue() |
frontend/src/components/settings/SandboxSettings.tsx | Modify | Filter visibility on OPENSHELL_API_KEY provider key |
frontend/src/components/status/ThreadSandboxStatus.tsx | Modify | Filter visibility on OPENSHELL_API_KEY provider key |
Storage
- Persistence layer: Existing
UserSettingsentity (LangGraph Store) - Namespace / table:
(user_id, "settings")—default_sandboxfield - Model pattern: Extends existing
SandboxTypeenum — no new tables or columns
Architectural Decisions
- Source of truth: Backend
SandboxTypeenum validates allowed values; frontend mirrors the union type. - State management: Existing React Query cache invalidation on user settings mutations.
- Auth / scoping: User-scoped via session
user_id.OPENSHELL_API_KEYis a sentinel (not a real API key) — OpenShell uses mTLS auth from~/.config/openshell/. - Sync gRPC calls:
openshellSDK uses synchronous gRPC. Acceptable for now (same pattern as Daytona's sync HTTP calls). Can wrap inasyncio.to_thread()as follow-up.
Documentation
- Full spec:
specs/spec-a-minimal-backend.md - Reference implementation pattern:
backend/src/agents/daytona.py
Development Setup
Dependencies
| Service | Address | Notes |
|---|---|---|
| Redis | localhost:6379 | Docker container |
| Postgres | localhost:5432 | Docker container |
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
OPENSHELL_API_KEY | No | None | Sentinel to enable OpenShell in UI (set any truthy value) |
OPENSHELL_GATEWAY | No | None | Override active OpenShell gateway cluster name |
OPENSHELL_SANDBOX_NAME | No | None | Connect to pre-existing named sandbox |
Commands
# See package.json / Makefile for scriptsWiki
⚠️ IMPORTANT: Wiki lives in a separate repo. Changes to@wikimust be committed directly to the wiki repo, not the main project repo.
Design Principles
- Simplicity is beauty, complexity is pain.
- ALWAYS look at the current codebase first — achieve the goal in the least amount of changes.
- TDD-first: write tests before implementation.
- Follow the Daytona provider pattern exactly — no new abstractions.
Validation Tools
- Load
agent-browserskill with screenshots to validate E2E. This validates test assumptions for completion promise.
Acceptance Criteria
-
openshell>=0.0.10added tobackend/pyproject.toml -
OPENSHELL_API_KEY,OPENSHELL_GATEWAY,OPENSHELL_SANDBOX_NAMEconstants added -
OpenShellBackend(BaseSandbox)class created inbackend/src/agents/openshell.py -
resolve_sandbox_backend()fallback chain: Daytona → OpenShell → State -
SandboxType.OPENSHELLenum value added - Error handling added in
llm_invoke(),stream_generator(),_execute_agent_stream() - Frontend
SandboxTypeunion includes"openshell" - OpenShell option visible in settings/status dropdowns when
OPENSHELL_API_KEYis set - All previous & new tests pass, validated using
agent-browserCLI - New code follows existing repo/service/route patterns (e.g., BaseRepo, ServiceContext)
- Related API and user documentation updated in the wiki repo (committed directly to wiki repo)