Install plugin runtimes into the sandbox

Claude plugins bring their own runtime requirements. claude-mem and others run
their hooks under bun, which the sandbox image does not carry, so installing
plugins left every prompt in the sandbox failing with a Bun not found hook
error - after setup had reported success, since the dependency only surfaces
when a hook fires.

AI_SBX_TOOLS lists mise tools to install globally in the sandbox and defaults
to bun. mise is already present and resolves these from GitHub releases, which
the default network policy allows. Setting the variable to an empty string
installs nothing.
This commit is contained in:
2026-07-31 09:12:00 -05:00
parent 6f4ba117b4
commit c195a82b82
2 changed files with 49 additions and 0 deletions
+17
View File
@@ -292,6 +292,7 @@ provider "aws" {
| `AI_SBX_MODE` | `clone` | `--clone` / `--direct` | | `AI_SBX_MODE` | `clone` | `--clone` / `--direct` |
| `AI_SBX_TOKEN_DAYS` | `30` | token expiry pre-filled on the form (1–366, or `none`) | | `AI_SBX_TOKEN_DAYS` | `30` | token expiry pre-filled on the form (1–366, or `none`) |
| `AI_SBX_TEMPLATE` | unset | `--template` / `--stock-template` | | `AI_SBX_TEMPLATE` | unset | `--template` / `--stock-template` |
| `AI_SBX_TOOLS` | `bun` | mise tools installed globally in the sandbox; empty installs none |
## Carrying your Claude configuration into the sandbox ## Carrying your Claude configuration into the sandbox
@@ -310,6 +311,22 @@ Copies each skill directory from `~/.claude/skills` (and `~/.agents/skills`,
mounted into every new sandbox. Symlinks and loose top-level files are skipped. Skills mounted into every new sandbox. Symlinks and loose top-level files are skipped. Skills
under `~/.claude/plugins/` are **not** scanned — only the top-level skills directory. under `~/.claude/plugins/` are **not** scanned — only the top-level skills directory.
**Plugin runtimes.** Plugins bring their own dependencies: several — claude-mem among
them — run their hooks under `bun`, which the sandbox image does not carry. A missing
runtime shows up as a hook error on *every* prompt rather than at install time:
```text
SessionStart:startup hook error
Failed with non-blocking status code: Error: Bun not found.
```
`AI_SBX_TOOLS` installs tools globally in the sandbox with mise, and defaults to `bun`
for exactly this reason. Add to it for other runtimes:
```toml
AI_SBX_TOOLS = "bun deno"
```
**Kits — for tools, env vars, network rules, and startup commands:** **Kits — for tools, env vars, network rules, and startup commands:**
```bash ```bash
+32
View File
@@ -7,6 +7,7 @@ DEFAULT_AGENT="${AI_SBX_AGENT:-claude}"
DEFAULT_MODE="${AI_SBX_MODE:-clone}" DEFAULT_MODE="${AI_SBX_MODE:-clone}"
DEFAULT_TOKEN_DAYS="${AI_SBX_TOKEN_DAYS:-30}" DEFAULT_TOKEN_DAYS="${AI_SBX_TOKEN_DAYS:-30}"
DEFAULT_TEMPLATE="${AI_SBX_TEMPLATE:-}" DEFAULT_TEMPLATE="${AI_SBX_TEMPLATE:-}"
DEFAULT_TOOLS="${AI_SBX_TOOLS:-bun}"
CLAUDE_HOME="${CLAUDE_HOME:-$HOME/.claude}" CLAUDE_HOME="${CLAUDE_HOME:-$HOME/.claude}"
# Only these leave the host. ~/.claude also holds OAuth credentials, shell # Only these leave the host. ~/.claude also holds OAuth credentials, shell
@@ -71,6 +72,10 @@ AGENTS.md, agents, commands, hooks, skills) into the sandbox and installs the
marketplaces and plugins your host has enabled. Credentials, transcripts and marketplaces and plugins your host has enabled. Credentials, transcripts and
history are never copied. Use "config" to re-apply after the host changes. history are never copied. Use "config" to re-apply after the host changes.
AI_SBX_TOOLS lists mise tools installed globally in the sandbox, defaulting
to bun because several Claude plugins run their hooks under it. Set it to an
empty string to install none.
Setup and run install mise in the sandbox and resolve the repository's Setup and run install mise in the sandbox and resolve the repository's
pinned tools, so the agent runs the same versions you do. A personal pinned tools, so the agent runs the same versions you do. A personal
mise config that must stay out of the repository goes in the per-repository mise config that must stay out of the repository goes in the per-repository
@@ -727,6 +732,31 @@ install_sandbox_claude_plugins() {
done < <(host_enabled_plugins "$settings") done < <(host_enabled_plugins "$settings")
} }
# Plugins bring their own runtime requirements - claude-mem and others run
# their hooks under bun, which the sandbox image does not carry - and a missing
# one surfaces as a hook error on every prompt rather than at install time.
install_sandbox_tools() {
local sandbox_home="$1"
[[ -n "$DEFAULT_TOOLS" ]] || return 0
local -a tools
read -r -a tools <<<"$DEFAULT_TOOLS"
((${#tools[@]})) || return 0
printf 'Installing sandbox tools: %s\n' "${tools[*]}"
# mise resolves these from GitHub releases, which the default network
# policy already allows. $HOME and $@ belong to the sandbox shell.
# shellcheck disable=SC2016
sbx exec "$SANDBOX_NAME" bash -lc '
export PATH="$HOME/.local/bin:$HOME/.local/share/mise/shims:$PATH"
mise use -g "$@" && mise reshim
' _ "${tools[@]}" </dev/null >/dev/null 2>&1 ||
printf 'Could not install sandbox tools: %s\n' "${tools[*]}" >&2
}
install_sandbox_mise() { install_sandbox_mise() {
local sandbox_home local sandbox_home
# shellcheck disable=SC2016 # shellcheck disable=SC2016
@@ -753,6 +783,8 @@ install_sandbox_mise() {
sbx cp "$host_mise" "$SANDBOX_NAME:$sandbox_home/.local/bin/mise" sbx cp "$host_mise" "$SANDBOX_NAME:$sandbox_home/.local/bin/mise"
fi fi
install_sandbox_tools "$sandbox_home"
local personal="$REPO_CONFIG_DIR/mise.local.toml" local personal="$REPO_CONFIG_DIR/mise.local.toml"
if [[ -f "$personal" ]]; then if [[ -f "$personal" ]]; then
sbx cp "$personal" "$SANDBOX_NAME:$REPO_ROOT/mise.local.toml" sbx cp "$personal" "$SANDBOX_NAME:$REPO_ROOT/mise.local.toml"