Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions packages/modal-infra/src/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,9 @@

# Secrets for LLM API keys - defined in Modal dashboard or CLI
# These are injected into sandboxes but never stored in snapshots
llm_secrets = modal.Secret.from_name(
"llm-api-keys",
required_keys=["ANTHROPIC_API_KEY"],
)
# Either ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN can be set
# OAuth token takes precedence (uses subscription billing, much cheaper)
llm_secrets = modal.Secret.from_name("llm-api-keys")

# Secrets for GitHub App - used for git operations (clone, push)
# These are used to generate installation tokens, NOT injected into sandboxes
Expand Down
4 changes: 2 additions & 2 deletions packages/modal-infra/src/images/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Image definitions for Open-Inspect sandboxes."""

from .base import OPENCODE_VERSION, base_image
from .base import CACHE_BUSTER, base_image

__all__ = ["OPENCODE_VERSION", "base_image"]
__all__ = ["CACHE_BUSTER", "base_image"]
38 changes: 19 additions & 19 deletions packages/modal-infra/src/images/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
- Debian slim base with git, curl, build-essential
- Node.js 22 LTS, pnpm, Bun runtime
- Python 3.12 with uv
- OpenCode CLI pre-installed
- Claude Code ACP adapter for agent communication
- Playwright with headless Chrome for visual verification
- Sandbox entrypoint and bridge code
"""
Expand All @@ -17,14 +17,9 @@
# Get the path to the sandbox code
SANDBOX_DIR = Path(__file__).parent.parent / "sandbox"

# Plugin is now bundled with sandbox code at /app/sandbox/inspect-plugin.js

# OpenCode version to install
OPENCODE_VERSION = "latest"

# Cache buster - change this to force Modal image rebuild
# v34: Replace print() with structured JSON logging in sandbox code
CACHE_BUSTER = "v34-structured-logging"
# v41: Cache tool titles from ToolCallStart for use in ToolCallUpdate
CACHE_BUSTER = "v45-acp-10mb-buffer"

# Base image with all development tools
base_image = (
Expand Down Expand Up @@ -64,6 +59,13 @@
"node --version",
"npm --version",
)
# Install GitHub CLI (gh)
.run_commands(
"curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg",
'echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null',
"apt-get update && apt-get install -y gh",
"gh --version",
)
# Install pnpm and Bun
.run_commands(
# Install pnpm globally
Expand All @@ -83,14 +85,13 @@
"playwright",
"pydantic>=2.0", # Required for sandbox types
"PyJWT[crypto]", # For GitHub App token generation (includes cryptography)
"agent-client-protocol", # Python ACP SDK for agent communication
)
# Install OpenCode CLI and plugin for custom tools
# Install Claude Code ACP adapter for agent communication
# Uses Zed's wrapper which bridges Claude Agent SDK <-> ACP protocol
.run_commands(
"npm install -g opencode-ai@latest",
"opencode --version || echo 'OpenCode installed'",
# Install @opencode-ai/plugin globally for custom tools
# This ensures tools can import the plugin without needing to run bun add
"npm install -g @opencode-ai/plugin@latest zod",
"npm install -g @zed-industries/claude-code-acp@latest",
"claude-code-acp --version || echo 'Claude Code ACP installed'",
)
# Install Playwright browsers (Chromium only to save space)
.run_commands(
Expand All @@ -101,8 +102,7 @@
.run_commands(
"mkdir -p /workspace",
"mkdir -p /app/plugins",
"mkdir -p /tmp/opencode",
"echo 'Image rebuilt at: v21-force-rebuild' > /app/image-version.txt",
f"echo 'Image rebuilt at: {CACHE_BUSTER}' > /app/image-version.txt",
)
# Set environment variables (including cache buster to force rebuild)
.env(
Expand All @@ -114,11 +114,11 @@
"PLAYWRIGHT_BROWSERS_PATH": "/root/.cache/ms-playwright",
"PYTHONPATH": "/app",
"SANDBOX_VERSION": CACHE_BUSTER,
# NODE_PATH for globally installed modules (used by custom tools)
"NODE_PATH": "/usr/lib/node_modules",
# Auto-approve all permissions in sandbox (running in isolated container)
"CLAUDE_CODE_SKIP_PERMISSIONS": "true",
}
)
# Add sandbox code to the image (includes plugin at /app/sandbox/inspect-plugin.js)
# Add sandbox code to the image
.add_local_dir(
str(SANDBOX_DIR),
remote_path="/app/sandbox",
Expand Down
Loading