docs: document Devin Claude bridge operations

This commit is contained in:
Lucas Israel
2026-07-27 10:55:55 -03:00
parent b0d4d64e6c
commit c3a024d3be
5 changed files with 226 additions and 41 deletions

View File

@@ -1843,6 +1843,18 @@ APP_LOG_TO_FILE=true
# ── Devin CLI binary path ──
# Used by: open-sse/executors/devin-cli.ts. Default: looked up via PATH.
# CLI_DEVIN_BIN=devin
# Agentic bridge-only binary override. The bridge still executes ACP stdio only.
# CLI_DEVIN_AGENTIC_BIN=devin
# Required isolated HOME for the agentic Devin child process.
# DEVIN_AGENTIC_HOME=/home/bridge
# Bounded ACP turn timeout in milliseconds. Default: 120000.
# DEVIN_AGENTIC_ACP_TIMEOUT_MS=120000
# Agentic bridge model aliases. Values must keep the devin-cli-agentic/ prefix.
# DEVIN_BRIDGE_MODEL=devin-cli-agentic/swe-1-7
# DEVIN_BRIDGE_SONNET_MODEL=devin-cli-agentic/swe-1-7
# DEVIN_BRIDGE_OPUS_MODEL=devin-cli-agentic/swe-1-7
# DEVIN_BRIDGE_HAIKU_MODEL=devin-cli-agentic/swe-1-7
# DEVIN_BRIDGE_SUBAGENT_MODEL=devin-cli-agentic/swe-1-7
# ── Command Code (custom CLI) callback ──
# Local port used for OAuth-style callbacks from the Command Code CLI helper.

View File

@@ -1,58 +1,160 @@
# Devin Claude Bridge
This bridge adds `devin-cli-agentic`, a fail-closed provider for local Claude Code
traffic on OmniRoute's Anthropic Messages surface.
`devin-cli-agentic` lets the real Claude Code runtime use OmniRoute's local Anthropic
Messages endpoint while the official Devin CLI supplies model responses over ACP stdio.
It does not modify the existing Anthropic, Claude OAuth, Claude Web, or `devin-cli`
providers.
## Implemented
## Architecture
- New Claude-format provider: `devin-cli-agentic`.
- New executor: `open-sse/executors/devin-cli-agentic.ts`.
- ACP stdio flow: `initialize`, `session/new`, `session/prompt`, `session/update`.
- Native Anthropic JSON responses for text and `tool_use`.
- Native Anthropic SSE lifecycle frames for streaming clients.
- Strict parser for one Devin-requested tool call using `<tool>{...}</tool>`.
- Offline unit tests with a mock Devin CLI.
- Local isolation preflight script.
## Not Implemented
- Docker Compose profiles are not complete in this attempt.
- Real Claude Code E2E inside a container is not proven in this attempt.
- Live Devin tests are not run; they remain opt-in via `ENABLE_LIVE_DEVIN_TESTS=1`.
- Session affinity is intentionally not implemented.
## Usage
Route Claude Code model aliases to `devin-cli-agentic/<model>`, for example:
```bash
ANTHROPIC_BASE_URL=http://localhost:20128
ANTHROPIC_AUTH_TOKEN=sk-local-devin-gateway
ANTHROPIC_DEFAULT_SONNET_MODEL=devin-cli-agentic/swe-1-7
```text
Claude Code 2.1.220 (isolated Linux container)
-> http://omniroute:20128/v1/messages
-> devin-cli-agentic (Claude-format, no-auth provider)
-> devin acp --agent-type summarizer (ACP v1 over stdio)
-> Devin account in the dedicated devin-auth volume (live profile only)
```
The executor uses `CLI_DEVIN_AGENTIC_BIN` first, then `CLI_DEVIN_BIN`, then PATH.
The serializer in `open-sse/executors/devin-agentic/serializer.ts` preserves `system`,
`text`, `tool_use`, `tool_result`, `thinking`, `redacted_thinking`, `tool_choice`, and the
tools supplied by Claude Code. Images and unknown content blocks fail explicitly. Large
tool results use a visible truncation marker.
## Isolation
The parser accepts one standalone `<tool>{...}</tool>` envelope per model turn. It checks
the requested name against the request's tool list, validates arguments against that
tool's JSON Schema, rejects mixed narrative/actions, and permits one bounded repair.
Claude Code executes the resulting Anthropic `tool_use`; Devin never executes those local
tools through this adapter.
Normal tests use a mock Devin binary and do not execute Claude Code. The bridge does
not read host Claude files. The preflight script requires local OmniRoute routing and
rejects Anthropic/Claude credential environment variables:
## Threat model and isolation
The bridge assumes the host contains an unrelated personal Claude installation and treats
all host Claude configuration and credentials as forbidden. The Compose services:
- run as UID/GID `10001:10001`, with a read-only root filesystem, all capabilities dropped,
and `no-new-privileges`;
- use `/home/bridge`, a dedicated named Claude config volume, separate OmniRoute data
volumes, and a separate `devin-auth` volume;
- mount only disposable `.sandbox` workspaces/evidence and the bridge test harness;
- do not mount the host home, SSH files, cloud credentials, Keychain, or Docker socket;
- provide an explicit environment and remove Anthropic OAuth/API/routing variables before
Claude Code and the Devin subprocess run;
- direct Claude Code inference only to `http://omniroute:20128` using a local OmniRoute key.
The `offline` network is internal, so no runtime container can reach the Internet. The
`live-devin` OmniRoute service is also attached only to that internal network; outbound
HTTP(S) goes through `network-guard`, whose only allowed suffixes are `.devin.ai` and
`.cognition.ai`. Anthropic, Claude, Statsig, Sentry, and every unrelated destination are
denied by default. The guard records decisions in `.sandbox/evidence/egress.jsonl`.
Run the executable proof at any time:
```bash
CLAUDE_CONFIG_DIR=.sandbox/claude-devin-isolated \
ANTHROPIC_BASE_URL=http://localhost:20128 \
ANTHROPIC_AUTH_TOKEN=sk-local-devin-gateway \
./scripts/devin-bridge/verify-anthropic-isolation
```
## Verification
It validates Compose topology, named config mounts, non-root/read-only settings, explicit
local routing, absence of sensitive environment variables, absence of the Docker socket,
and failed TCP access to `api.anthropic.com` and `claude.ai`. The wire contract separately
stops the mock ACP process and confirms an explicit error with no fallback.
## Reproducible offline validation
The normal automated path does not need a Devin account and has no runtime Internet:
```bash
./scripts/devin-bridge/build
./scripts/devin-bridge/test-unit
npm test
./scripts/devin-bridge/test-contract
./scripts/devin-bridge/test-e2e-mock
./scripts/devin-bridge/verify-anthropic-isolation
```
In this workspace, `node_modules` was missing during implementation, so `tsx` could
not be resolved until dependencies are installed.
`test-e2e-mock` copies `tests/fixtures/devin-bridge/e2e-workspace` into `.sandbox`, then
runs the pinned Claude Code binary. The fixture contains `CLAUDE.md`, a project skill, a
slash command, hooks, source, and tests. The deterministic ACP mock asks Claude Code to
locate, read, edit, test, observe a failure, repair, retest, and finish. Evidence stays
unversioned in `.sandbox/evidence`.
## Devin login and live use
Authentication uses only the official CLI inside the dedicated volume. It never imports a
host session:
```bash
ENABLE_LIVE_DEVIN_TESTS=1 ./scripts/devin-bridge/login-devin
```
After login, the live test checks `devin auth status`, obtains the account's machine-readable
model list with `devin models list --format json`, selects a returned model identifier, and
runs three disposable Claude Code scenarios:
```bash
ENABLE_LIVE_DEVIN_TESTS=1 ./scripts/devin-bridge/test-live-devin
```
For interactive use, `launch` repeats isolation, auth, and model discovery checks before
starting the containerized Claude Code runtime:
```bash
./scripts/devin-bridge/launch
```
Optional model aliases live in `.env.devin-bridge.example`. `DEVIN_BRIDGE_MODEL` controls
the main model; `DEVIN_BRIDGE_SONNET_MODEL`, `DEVIN_BRIDGE_OPUS_MODEL`,
`DEVIN_BRIDGE_HAIKU_MODEL`, and `DEVIN_BRIDGE_SUBAGENT_MODEL` allow explicit mapping. Every
value must retain the `devin-cli-agentic/` prefix. The live test uses a model returned by
the current Devin account instead of trusting the example value.
## Updating pinned tools
The image pins Node, Claude Code, and Devin CLI in `docker/devin-bridge/Dockerfile` and
`docker/devin-bridge/compose.yml`. To update:
1. change the explicit version arguments;
2. update both architecture-specific Devin archive checksums from the official artifact;
3. run the complete offline command set above;
4. confirm the artifact versions inside the rebuilt image;
5. run the live suite only after offline proof remains green.
Do not replace the checksum with an unverified download or install either CLI globally on
the host.
## Diagnosis and cleanup
- `docker compose -f docker/devin-bridge/compose.yml --profile offline logs omniroute`
shows local routing and sanitized executor failures.
- `.sandbox/evidence/mock-acp.jsonl` records deterministic offline provider actions.
- `.sandbox/evidence/claude-stream.jsonl` records the real Claude Code offline run.
- `.sandbox/evidence/egress.jsonl` records live guard decisions without tokens.
- An ACP timeout, malformed frame, unavailable binary/model, or process exit is an explicit
`502`; it never selects a second provider.
Stop containers and networks while preserving login/config volumes:
```bash
./scripts/devin-bridge/clean
```
Remove the complete bridge-owned environment, including named volumes:
```bash
./scripts/devin-bridge/clean --all
```
`.sandbox` can then be deleted independently; it contains only disposable fixtures,
isolated databases, and evidence.
## Limits
- ACP context is reconstructed from each Anthropic request; there is no persistent process
or session affinity.
- One tool call is supported per model response; parallel tool calls are rejected.
- Images are explicitly unsupported. Vision, thinking output, effort controls, and a 1M
context window are not advertised as bridge capabilities.
- SSE has valid Anthropic lifecycle events but is rendered after the bounded ACP response is
collected; ACP chunks are not forwarded incrementally to the client.
- The strict parser depends on the Devin model following the documented tool envelope. One
repair is attempted before the request fails.
- Offline proof is deterministic. Live readiness requires a successful official Devin login
and all three live scenarios; it must not be inferred from offline results.

View File

@@ -0,0 +1,63 @@
# Devin Claude Bridge Progress
Updated: 2026-07-27
## Baseline
- Fork version: `3.8.49`.
- Starting branch: `release/v3.8.49`.
- Starting commit: `ed7db3ee5f89a144b2d931d8605534522f83de30`.
- Fixed runtime artifacts: Node `26.0.0`, Claude Code `2.1.220`, Devin CLI `3000.2.17`.
- Existing `devin-cli` remains unchanged; the new path is the separate
`devin-cli-agentic` provider.
## Proved offline
- Focused unit and ACP suite: 20 tests passed.
- Anthropic wire suite: non-streaming JSON, SSE event order, `tool_use`, direct
`tool_result` continuation, ACP error, and early process exit passed.
- Final container build completed with the pinned CLIs and the production OmniRoute build.
- Artifact inspection confirmed Node `26.0.0`, Claude Code `2.1.220`, and Devin CLI
`3000.2.17` while the container had no network.
- Runtime isolation verifier passed in static and container checks.
- Real Claude Code offline E2E passed in 9 turns. It loaded `CLAUDE.md`, discovered the
project skill and slash command, fired hooks, requested `Skill`, `Bash`, `Read`, and
`Edit`, observed a failing test, corrected the edit, reran the test successfully, and
returned the fixture's final success marker.
- Every offline inference action recorded by the ACP fixture names only
`devin-cli-agentic`; provider termination returned an explicit error.
Evidence is generated under `.sandbox/evidence` and is intentionally ignored by Git.
## Regression status
- Focused ESLint and `typecheck:core` passed.
- The complete `npm run check` reached the unit suite after lint, but the repository test
runner did not terminate after `quota-redis-store.test.ts`: an `ioredis` client kept
reconnecting to an unavailable local Redis endpoint. The runner was interrupted after
repeated `ECONNREFUSED` events, so this command is not reported as passed.
- Documentation accuracy checks passed before this final status update and are rerun after it.
- The fresh image rebuild and dedicated Devin volume ownership retry passed.
## Live Devin
Not passed. After the ownership repair, a fresh `devin-auth` volume was mounted and read by
the non-root bridge user. The official CLI then reported that it was not logged in. Dynamic
model discovery and the three live scenarios therefore did not run, and no live model request
was made.
The login command resumes the complete live suite automatically after authentication:
```bash
ENABLE_LIVE_DEVIN_TESTS=1 ./scripts/devin-bridge/login-devin
```
Do not report the bridge as live-ready until `devin auth status`, dynamic model discovery,
and the three scenarios in `test-live-devin` all succeed.
## Safety record
No host Claude executable, configuration, login, OAuth token, Keychain, or Anthropic API was
used. The first pre-isolation unit attempt initialized the repository's default OmniRoute
database at `/Users/lucasisrael/.omniroute/storage.sqlite`; it was not rolled back or touched
again. All subsequent bridge commands set isolated database paths under `.sandbox`.

View File

@@ -377,6 +377,14 @@ Controls how OmniRoute discovers and launches CLI sidecars (Claude Code, Codex,
| `CLI_QODER_BIN` | `qoder` | `src/shared/services/cliRuntime.ts` | Custom path to Qoder CLI binary. |
| `CLI_QWEN_BIN` | `qwen` | `src/shared/services/cliRuntime.ts` | Custom path to the Qwen Code CLI binary. |
| `CLI_DEVIN_BIN` | `devin` | `open-sse/executors/devin-cli.ts` | Custom path to the Devin CLI binary (v3.8.0). Used by the Windsurf/Devin executor. |
| `CLI_DEVIN_AGENTIC_BIN` | `devin` | `open-sse/executors/devin-cli-agentic.ts` | Agentic bridge-only Devin CLI override. The executor accepts only the local ACP stdio upstream. |
| `DEVIN_AGENTIC_HOME` | _(required)_ | `open-sse/executors/devin-cli-agentic.ts` | Absolute isolated home for the agentic Devin subprocess; accepted bridge paths are `/home/bridge` and task-local `.sandbox` paths. |
| `DEVIN_AGENTIC_ACP_TIMEOUT_MS` | `120000` | `open-sse/executors/devin-cli-agentic.ts` | Maximum duration of one Devin ACP turn before the bridge terminates the child and returns an explicit timeout. |
| `DEVIN_BRIDGE_MODEL` | `devin-cli-agentic/swe-1-7` | `docker/devin-bridge/compose.yml` | Main Claude Code model alias for the isolated bridge. The live harness replaces the example with a model returned by the current Devin account. |
| `DEVIN_BRIDGE_SONNET_MODEL` | `DEVIN_BRIDGE_MODEL` | `docker/devin-bridge/compose.yml` | Isolated bridge alias used when Claude Code requests its Sonnet default. |
| `DEVIN_BRIDGE_OPUS_MODEL` | `DEVIN_BRIDGE_MODEL` | `docker/devin-bridge/compose.yml` | Isolated bridge alias used when Claude Code requests its Opus default. |
| `DEVIN_BRIDGE_HAIKU_MODEL` | `DEVIN_BRIDGE_MODEL` | `docker/devin-bridge/compose.yml` | Isolated bridge alias used when Claude Code requests its Haiku default. |
| `DEVIN_BRIDGE_SUBAGENT_MODEL` | `DEVIN_BRIDGE_MODEL` | `docker/devin-bridge/compose.yml` | Isolated bridge alias used for Claude Code subagents. |
| `AUGGIE_BIN` | `auggie` | `open-sse/executors/auggie.ts` | Absolute-path override for the Augment (Auggie) CLI binary used by the local `auggie` provider. Falls back to `CLI_AUGGIE_BIN`, then a PATH lookup. |
| `CLI_AUGGIE_BIN` | `auggie` | `open-sse/executors/auggie.ts` | Alias override for the Augment (Auggie) CLI binary path (checked after `AUGGIE_BIN`). |
| `HERMES_HOME` | `~/.hermes` | `src/lib/cli-helper/config-generator/hermesHome.ts` | Hermes Agent home directory where OmniRoute reads/writes the Hermes CLI config. Matches the env var the Hermes PowerShell installer sets on Windows (`%LOCALAPPDATA%\hermes`). |

View File

@@ -60,9 +60,9 @@ Focused unit tests cover serialization, tool parsing, validation, Anthropic JSON
## Mandatory Runtime Isolation
The bridge runs only through `docker/devin-bridge/compose.yml`. The runtime image is non-root, uses a private `/home/bridge`, and mounts only this fork read-only plus a disposable workspace. It never mounts the host home, Docker socket, SSH, cloud credentials, or global Claude configuration. The container receives an explicit environment allowlist; the executor also constructs an allowlisted child environment instead of copying `process.env`.
The bridge runs only through `docker/devin-bridge/compose.yml`. The runtime image is non-root, uses a private `/home/bridge`, and mounts only disposable workspaces, evidence, and bridge harness files. Application source is copied into the image. It never mounts the host home, Docker socket, SSH, cloud credentials, or global Claude configuration. The container receives an explicit environment allowlist; the executor also constructs an allowlisted child environment instead of copying `process.env`.
Build-time network access installs Claude Code `2.1.220` and Devin CLI `3000.2.17` with pinned integrity/checksum. Runtime profiles are separate: `offline` has `network_mode: none` for Claude plus an internal-only OmniRoute/mock network; `live-devin` has controlled egress through a DNS/proxy guard that denies Anthropic, Claude, Statsig, and Sentry domains and records attempted destinations. Devin authentication lives only in the named `devin-auth` volume. Claude configuration lives in a different named volume and is initialized empty.
Build-time network access installs Claude Code `2.1.220` and Devin CLI `3000.2.17` with pinned integrity/checksum. Runtime profiles are separate: `offline` uses only an internal Compose network; `live-devin` exposes egress only through a proxy guard whose allowlist contains Devin/Cognition suffixes and whose default is denial. Devin authentication lives only in the named `devin-auth` volume. Claude configuration lives in a different named volume and is initialized empty.
## Fail-Closed Routing