Skip to content

Commit 041481a

Browse files
authored
Merge pull request #145 from thevibeworks/feat/gemini-cli
feat: add tmux bridge, gemini support, and build resilience
2 parents b3c91a5 + 5d38e65 commit 041481a

19 files changed

Lines changed: 2343 additions & 395 deletions

AGENTS.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
This file provides guidance to codex, Claude Code when working with code in this repository.
22

3+
## Deva Architecture: Container-Based Agent Sandboxing
4+
5+
**CRITICAL DESIGN PATTERN**: Deva purposely runs ALL agents inside Docker containers. The container IS the sandbox.
6+
7+
- Each agent (claude, codex, gemini) runs in isolated container environment
8+
- Agent internal sandboxes/permission systems are DISABLED:
9+
- claude: `--dangerously-skip-permissions`
10+
- gemini: `--yolo` flag
11+
- codex: equivalent unrestricted mode
12+
- Container provides security boundary instead of agent-level prompts
13+
- Result: No interactive permission prompts while maintaining isolation
14+
15+
**Why**: Avoids permission fatigue in trusted workspaces while keeping agents containerized for safety.
16+
317
## We're following Issue-Based Development (IBD) workflow
418
1. Before running any Git/GitHub CLI `Bash` command (`git commit`, `gh issue create`, `gh pr create`, etc.), open the corresponding file in @workflows to review required steps.
519
2. Always apply the exact templates or conventions from the following files:
@@ -121,6 +135,50 @@ Model aliases are automatically converted to appropriate formats (API model name
121135
- Requires explicit confirmation (`yes`) to proceed
122136
- Protects users from accidentally giving Claude access to all personal files
123137

138+
**Docker Socket Warning** (SECURITY-SENSITIVE):
139+
By default, `/var/run/docker.sock` is auto-mounted if present. This grants full Docker API access to the container - effectively equivalent to root on the host. The "container as sandbox" model is weakened when Docker socket is mounted.
140+
141+
Implications:
142+
- Agent can start/stop any container on host
143+
- Agent can mount any host path into new containers
144+
- Agent can escape to host via privileged container creation
145+
146+
Mitigations:
147+
- Use `--no-docker` flag to disable auto-mount
148+
- Set `DEVA_NO_DOCKER=1` environment variable
149+
- Only mount when Docker-in-Docker workflows are required
150+
151+
## Bridges (privileged)
152+
153+
Deva's container IS the sandbox. Bridges punch controlled holes back to the host for specific integrations. Each bridge has TWO components: host-side and container-side.
154+
155+
| Bridge | Host Command | Container Command | Risk |
156+
|--------|--------------|-------------------|------|
157+
| Docker | (auto-mount `/var/run/docker.sock`) | `docker ...` | Root-equivalent on host |
158+
| tmux | `deva-bridge-tmux-host` | `deva-bridge-tmux` | Host command execution |
159+
160+
**tmux bridge**: Connect container tmux client to host tmux server.
161+
- Problem: Unix socket mount fails across macOS<->Linux kernel boundary
162+
- Solution: socat TCP proxy (host) + socat Unix socket (container)
163+
- Security: Container gains full tmux control (send-keys, run-shell, scrollback)
164+
165+
Usage:
166+
```bash
167+
# Host (macOS)
168+
./scripts/deva-bridge-tmux-host
169+
170+
# Container
171+
deva-bridge-tmux
172+
tmux -S /tmp/host-tmux.sock list-sessions
173+
```
174+
175+
Environment variables:
176+
- `DEVA_BRIDGE_BIND`: host bind address (default: 127.0.0.1)
177+
- `DEVA_BRIDGE_PORT`: TCP port (default: 41555)
178+
- `DEVA_BRIDGE_HOST`: container's host address (default: host.docker.internal)
179+
- `DEVA_BRIDGE_SOCKET`: host tmux socket path (default: auto-detected)
180+
- `DEVA_BRIDGE_SOCK`: container local socket (default: /tmp/host-tmux.sock)
181+
124182
## Docker Architecture Details
125183

126184
### Volume Mounting Strategy

CLAUDE.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
44

5+
## Deva Architecture: Container-Based Agent Sandboxing
6+
7+
**CRITICAL DESIGN PATTERN**: Deva purposely runs ALL agents inside Docker containers. The container IS the sandbox.
8+
9+
- Each agent (claude, codex, gemini) runs in isolated container environment
10+
- Agent internal sandboxes/permission systems are DISABLED (e.g., claude --dangerously-skip-permissions, GEMINI_SANDBOX=false)
11+
- Container provides security boundary instead of agent-level prompts
12+
- Result: No interactive permission prompts while maintaining isolation
13+
14+
**Why**: Avoids permission fatigue in trusted workspaces while keeping agents containerized for safety.
15+
516
## We're following Issue-Based Development (IBD) workflow
617
1. Before running any Git/GitHub CLI `Bash` command (`git commit`, `gh issue create`, `gh pr create`, etc.), open the corresponding file in @workflows to review required steps.
718
2. Always apply the exact templates or conventions from the following files:

DEV-LOGS.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,24 @@
1414
- Reference issue numbers in the format `#<issue-number>` for easy linking.
1515

1616

17+
# [2026-01-07] Dev Log: Fix version-upgrade build resilience
18+
- Why: `make versions-up` exited 56 during GitHub API changelog fetch - GitHub API 403 rate limit (60/hour) from unauthenticated curl
19+
- What:
20+
- Changed `fetch_github_releases()` and `fetch_recent_github_releases()` in `scripts/release-utils.sh` from `curl` to `gh api` for authenticated requests
21+
- All changelog fetch functions now fail gracefully with `{ echo "(fetch failed)"; return 0; }` instead of `|| return` (was causing `set -e` script abort)
22+
- Added fallback in `load_versions()` - network fetch failure uses current image version instead of empty string
23+
- Added pre-build version check in `scripts/version-upgrade.sh` - warns about missing versions but proceeds with build
24+
- Result: Build script resilient to transient network failures and GitHub rate limits. Changelog display is best-effort, won't block builds.
25+
26+
**Files changed**:
27+
- `scripts/release-utils.sh` (lines 175, 221, 452, 480)
28+
- `scripts/version-upgrade.sh` (lines 82-95)
29+
30+
# [2025-11-27] Dev Log: Docker-in-Docker auto-mount support
31+
- Why: Common dev workflow need - testing containers, building images, CI/CD simulation inside deva environments
32+
- What: Auto-mount Docker socket (`/var/run/docker.sock`) by default with graceful detection, opt-out via `--no-docker` flag or `DEVA_NO_DOCKER=1`, quick permission fix (chmod 666) for deva user access
33+
- Result: DinD works out-of-box on Linux/macOS/WSL2, no manual socket mounting needed, aligns with YOLO philosophy (make it work, container is the boundary)
34+
1735
# [2025-10-26] Dev Log: Custom credential files via --auth-with
1836
- Why: Users have multiple credential files, needed direct path support beyond predefined auth methods
1937
- What: `--auth-with /path/to/creds.json` now works, auto-backup existing credentials, workspace session tracking in `~/.config/deva/sessions/*.json`

Dockerfile

Lines changed: 79 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
3030
openssh-client rsync \
3131
shellcheck bat fd-find silversearcher-ag \
3232
vim \
33-
procps psmisc zsh
33+
procps psmisc zsh socat \
34+
libevent-dev libncurses-dev bison
3435

3536
RUN git lfs install --system
3637

@@ -48,23 +49,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
4849
RUN curl -fsSL https://bun.sh/install | bash && \
4950
ln -s /root/.bun/bin/bun /usr/local/bin/bun
5051

51-
# Install Copilot API branch with GPT-5 Codex responses support (caozhiyuan fork)
52-
# feature/responses-api adds: GPT-5-Codex support, model reasoning, token caching, enhanced streaming
53-
ARG COPILOT_API_REPO=https://github.com/caozhiyuan/copilot-api.git
54-
ARG COPILOT_API_BRANCH=feature/responses-api
55-
ARG COPILOT_API_COMMIT=83cdfde17d7d3be36bd2493cc7592ff13be4928d
56-
57-
RUN --mount=type=cache,target=/root/.npm,sharing=locked \
58-
npm install -g npm@latest pnpm && \
59-
git clone --branch "${COPILOT_API_BRANCH}" "${COPILOT_API_REPO}" /tmp/copilot-api && \
60-
cd /tmp/copilot-api && \
61-
git checkout "${COPILOT_API_COMMIT}" && \
62-
git log --oneline -5 && \
63-
bun install --frozen-lockfile && bun run build && \
64-
cd /tmp && npm install -g --ignore-scripts /tmp/copilot-api && \
65-
rm -rf /tmp/copilot-api && \
66-
npm cache clean --force
67-
52+
# Install stable runtimes BEFORE volatile packages to maximize cache reuse
6853
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
6954

7055
# Pre-install Python 3.14t (free-threaded) for uv
@@ -77,6 +62,26 @@ RUN --mount=type=cache,target=/tmp/go-cache,sharing=locked \
7762
wget -q https://go.dev/dl/go1.22.0.linux-${GO_ARCH}.tar.gz && \
7863
tar -C /usr/local -xzf go1.22.0.linux-${GO_ARCH}.tar.gz
7964

65+
# Install Copilot API (ericc-ch fork with latest features)
66+
# Placed at end of runtimes stage to avoid invalidating cache for stable runtimes
67+
ARG COPILOT_API_REPO=https://github.com/ericc-ch/copilot-api.git
68+
ARG COPILOT_API_BRANCH=master
69+
ARG COPILOT_API_COMMIT=master
70+
ARG COPILOT_API_VERSION
71+
72+
LABEL org.opencontainers.image.copilot_api_version=${COPILOT_API_VERSION}
73+
74+
RUN --mount=type=cache,target=/root/.npm,sharing=locked \
75+
npm install -g npm@latest pnpm && \
76+
git clone --branch "${COPILOT_API_BRANCH}" "${COPILOT_API_REPO}" /tmp/copilot-api && \
77+
cd /tmp/copilot-api && \
78+
git checkout "${COPILOT_API_COMMIT}" && \
79+
git log --oneline -5 && \
80+
bun install --frozen-lockfile && bun run build && \
81+
cd /tmp && npm install -g --ignore-scripts /tmp/copilot-api && \
82+
rm -rf /tmp/copilot-api && \
83+
npm cache clean --force
84+
8085
FROM runtimes AS cloud-tools
8186

8287
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
@@ -119,6 +124,24 @@ RUN --mount=type=cache,target=/tmp/delta-cache,sharing=locked \
119124
mv delta-0.18.2-${DELTA_ARCH}-unknown-linux-gnu/delta /usr/local/bin/ && \
120125
rm -rf delta-0.18.2-${DELTA_ARCH}-unknown-linux-gnu*
121126

127+
# Install tmux from source (protocol version must match host for socket bridge)
128+
# Same major.minor usually works; exact match is pragmatic, not required.
129+
ARG TMUX_VERSION=3.6a
130+
ARG TMUX_SHA256=b6d8d9c76585db8ef5fa00d4931902fa4b8cbe8166f528f44fc403961a3f3759
131+
RUN --mount=type=cache,target=/tmp/tmux-cache,sharing=locked \
132+
set -eu && \
133+
cd /tmp/tmux-cache && \
134+
TARBALL="tmux-${TMUX_VERSION}.tar.gz" && \
135+
wget -q "https://github.com/tmux/tmux/releases/download/${TMUX_VERSION}/${TARBALL}" && \
136+
echo "${TMUX_SHA256} ${TARBALL}" | sha256sum -c - && \
137+
tar -xzf "${TARBALL}" && \
138+
cd "tmux-${TMUX_VERSION}" && \
139+
./configure --prefix=/usr/local && \
140+
make -j"$(nproc)" && \
141+
make install && \
142+
rm -rf /tmp/tmux-cache/tmux-* && \
143+
hash -r && \
144+
tmux -V
122145

123146
ENV NPM_CONFIG_FETCH_RETRIES=5 \
124147
NPM_CONFIG_FETCH_RETRY_FACTOR=2 \
@@ -146,31 +169,12 @@ RUN mkdir -p "$DEVA_HOME/.npm-global" && \
146169

147170
# Set npm configuration for deva user and install CLI tooling
148171
USER $DEVA_USER
149-
ARG CLAUDE_CODE_VERSION
150-
ARG CODEX_VERSION
151-
152-
# Record key tool versions as labels for quick inspection
153-
LABEL org.opencontainers.image.claude_code_version=${CLAUDE_CODE_VERSION}
154-
LABEL org.opencontainers.image.codex_version=${CODEX_VERSION}
155172

156173
# Speed up npm installs and avoid noisy audits/funds prompts
157174
ENV NPM_CONFIG_AUDIT=false \
158175
NPM_CONFIG_FUND=false
159176

160-
# Use BuildKit cache for npm to speed up repeated builds
161-
RUN --mount=type=cache,target=/home/deva/.npm,uid=${DEVA_UID},gid=${DEVA_GID},sharing=locked \
162-
npm config set prefix "$DEVA_HOME/.npm-global" && \
163-
npm install -g --no-audit --no-fund \
164-
@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION} \
165-
@mariozechner/claude-trace \
166-
@openai/codex@${CODEX_VERSION} && \
167-
npm cache clean --force && \
168-
npm list -g --depth=0 @anthropic-ai/claude-code @openai/codex || true
169-
170-
# Install Go tools for Atlassian integration (Confluence/Jira/Bitbucket)
171-
RUN go install github.com/lroolle/atlas-cli/cmd/atl@5f6a20c4d164bf6fe6f5c60f9ac12dfccf210758 && \
172-
sudo mv $HOME/go/bin/atl /usr/local/bin/
173-
177+
# Install stable components BEFORE ARG declarations to maximize cache reuse
174178
RUN git clone --depth=1 https://github.com/ohmyzsh/ohmyzsh "$DEVA_HOME/.oh-my-zsh" && \
175179
git clone --depth=1 https://github.com/zsh-users/zsh-autosuggestions "$DEVA_HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions" && \
176180
git clone --depth=1 https://github.com/zsh-users/zsh-syntax-highlighting.git "$DEVA_HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting"
@@ -186,12 +190,47 @@ RUN echo 'export ZSH="$HOME/.oh-my-zsh"' > "$DEVA_HOME/.zshrc" && \
186190
RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \
187191
$DEVA_HOME/.local/bin/uv python install 3.14t
188192

193+
# Declare ARGs immediately before usage to minimize cache invalidation
194+
ARG CLAUDE_CODE_VERSION
195+
ARG CODEX_VERSION
196+
ARG GEMINI_CLI_VERSION=latest
197+
198+
# Record key tool versions as labels for quick inspection
199+
LABEL org.opencontainers.image.claude_code_version=${CLAUDE_CODE_VERSION}
200+
LABEL org.opencontainers.image.codex_version=${CODEX_VERSION}
201+
LABEL org.opencontainers.image.gemini_cli_version=${GEMINI_CLI_VERSION}
202+
203+
# Install CLI tools via npm
204+
RUN --mount=type=cache,target=/home/deva/.npm,uid=${DEVA_UID},gid=${DEVA_GID},sharing=locked \
205+
npm config set prefix "$DEVA_HOME/.npm-global" && \
206+
npm install -g --no-audit --no-fund \
207+
@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION} \
208+
@mariozechner/claude-trace \
209+
@openai/codex@${CODEX_VERSION} \
210+
@google/gemini-cli@${GEMINI_CLI_VERSION} && \
211+
npm cache clean --force && \
212+
npm list -g --depth=0 @anthropic-ai/claude-code @openai/codex @google/gemini-cli || true
213+
214+
# Volatile packages: Install at the end to avoid cascading rebuilds
215+
ARG ATLAS_CLI_VERSION=main
216+
217+
LABEL org.opencontainers.image.atlas_cli_version=${ATLAS_CLI_VERSION}
218+
219+
# Install atlas-cli binary + skill via upstream install.sh
220+
# - Uses prebuilt release tarball (faster than go install)
221+
# - Falls back to go install if no prebuilt for platform
222+
# - Installs skill with proper structure (SKILL.md + references/)
223+
RUN curl -fsSL "https://raw.githubusercontent.com/lroolle/atlas-cli/${ATLAS_CLI_VERSION}/install.sh" \
224+
| bash -s -- --skill-dir $DEVA_HOME/.skills
225+
189226
USER root
190227

191228
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
229+
COPY scripts/deva-bridge-tmux /usr/local/bin/deva-bridge-tmux
192230

193-
RUN chmod +x /usr/local/bin/docker-entrypoint.sh && \
194-
chmod -R +x /usr/local/bin/scripts || true
231+
RUN chmod 755 /usr/local/bin/docker-entrypoint.sh && \
232+
chmod 755 /usr/local/bin/deva-bridge-tmux && \
233+
chmod -R 755 /usr/local/bin/scripts || true
195234

196235
WORKDIR /root
197236

Dockerfile.rust

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,12 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
2929
libpng-dev libjpeg-dev \
3030
libudev-dev \
3131
libproc2-dev \
32-
libzmq3-dev libzmq5 libczmq-dev
32+
libzmq3-dev libzmq5 libczmq-dev && \
33+
curl -fsSL 'https://packages.clickhouse.com/rpm/lts/repodata/repomd.xml.key' | gpg --dearmor -o /usr/share/keyrings/clickhouse-keyring.gpg && \
34+
ARCH=$(dpkg --print-architecture) && \
35+
echo "deb [signed-by=/usr/share/keyrings/clickhouse-keyring.gpg arch=${ARCH}] https://packages.clickhouse.com/deb stable main" > /etc/apt/sources.list.d/clickhouse.list && \
36+
apt-get update && \
37+
apt-get install -y --no-install-recommends clickhouse-client
3338

3439
RUN --mount=type=cache,target=/tmp/rust-cache,sharing=locked \
3540
set -euxo pipefail && \

0 commit comments

Comments
 (0)