mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-07 07:42:13 +03:00
feat(executors): add isolated Claude Code bridge over Devin ACP (#8914)
Validated in post-merge-train sweep (boards clean on release/v3.8.50 tip)
This commit is contained in:
181
docs/DEVIN_CLAUDE_BRIDGE.md
Normal file
181
docs/DEVIN_CLAUDE_BRIDGE.md
Normal file
@@ -0,0 +1,181 @@
|
||||
# Devin Claude Bridge
|
||||
|
||||
`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.
|
||||
|
||||
> **Current status: offline and live validated.** The pinned Claude Code `2.1.220` completed
|
||||
> three isolated scenarios through Devin CLI `3000.2.17` and model
|
||||
> `swe-1-7-lightning`. The final live run proved client-owned `Read`, `Edit`, and `Bash`
|
||||
> turns, successful `npm test` results, project command and skill discovery, Devin-only
|
||||
> routing, and zero Claude egress.
|
||||
|
||||
## Architecture
|
||||
|
||||
```text
|
||||
Claude Code 2.1.220 (isolated non-root Linux container)
|
||||
-> http://omniroute:20128/v1/messages
|
||||
-> devin-cli-agentic (Claude-format, no-auth provider)
|
||||
-> devin acp --agent-type summarizer (official ACP stdio, no Devin tools)
|
||||
-> Devin account in the dedicated devin-auth volume
|
||||
```
|
||||
|
||||
The official CLI's default ACP agent can execute its own tools, so this bridge does not use
|
||||
it. It starts the fixed `summarizer` ACP agent, whose official CLI mode has no tools, and
|
||||
frames the serialized Anthropic request as an execution trace. When another Claude-owned
|
||||
action is needed, the response must contain exactly one client tool envelope. Any ACP
|
||||
`tool_call` or `tool_call_update` is rejected before a response can be reported as
|
||||
successful.
|
||||
|
||||
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 blocks fail explicitly. Large tool results
|
||||
use a visible truncation marker.
|
||||
|
||||
The parser accepts one standalone `<tool>{...}</tool>` envelope per model turn. It checks
|
||||
the 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 then
|
||||
executes the resulting Anthropic `tool_use` locally and sends the `tool_result` back through
|
||||
OmniRoute.
|
||||
|
||||
## Isolation and threat model
|
||||
|
||||
The host's Claude installation, account, and configuration are out of scope and treated as
|
||||
forbidden. The Compose services:
|
||||
|
||||
- run as UID/GID `10001:10001`, with a read-only root filesystem, dropped capabilities, and
|
||||
`no-new-privileges`;
|
||||
- use a private `/home/bridge`, a dedicated Claude config volume, isolated OmniRoute data,
|
||||
and a separate `devin-auth` volume;
|
||||
- mount only disposable `.sandbox` workspaces/evidence;
|
||||
- do not mount the host home, Keychain, SSH, cloud credentials, or Docker socket;
|
||||
- construct explicit environments and remove Anthropic API/OAuth/routing variables;
|
||||
- direct Claude Code inference only to `http://omniroute:20128` with a local-only key.
|
||||
|
||||
The offline profile uses an internal network. In the live profile, OmniRoute reaches the
|
||||
official Devin endpoints only through `network-guard`; unrelated destinations are denied.
|
||||
Claude Code has a separate deny-all egress guard and can reach only the local OmniRoute
|
||||
service through `NO_PROXY`. Guard audit files are mounted only by their guard process. The
|
||||
scripts verify file ownership, mode, link count, and every decision before exporting
|
||||
token-free evidence.
|
||||
|
||||
Run the isolation proof independently:
|
||||
|
||||
```bash
|
||||
./scripts/devin-bridge/verify-anthropic-isolation
|
||||
```
|
||||
|
||||
It validates topology, named mounts, non-root/read-only settings, explicit local routing,
|
||||
absence of sensitive environment variables, absence of the Docker socket, blocked access to
|
||||
`api.anthropic.com` and `claude.ai`, Devin-only provider selection, and explicit failure when
|
||||
the ACP backend is unavailable.
|
||||
|
||||
## First-time setup and normal use
|
||||
|
||||
Build the pinned image:
|
||||
|
||||
```bash
|
||||
./scripts/devin-bridge/build
|
||||
```
|
||||
|
||||
Authenticate only the isolated Devin volume:
|
||||
|
||||
```bash
|
||||
ENABLE_LIVE_DEVIN_TESTS=1 ./scripts/devin-bridge/login-devin
|
||||
```
|
||||
|
||||
The login command uses the official manual-token flow intended for remote/container
|
||||
environments. The value is entered directly into the CLI prompt; it is not passed as a
|
||||
process argument, written to Git, or copied from the host.
|
||||
|
||||
Launch the isolated Claude Code runtime:
|
||||
|
||||
```bash
|
||||
./scripts/devin-bridge/launch
|
||||
```
|
||||
|
||||
`launch` rechecks isolation, Devin authentication, and model discovery before starting the
|
||||
containerized Claude Code. It never runs the host's Claude executable. Model aliases can be
|
||||
set in `.env.devin-bridge`; every configured value must keep the
|
||||
`devin-cli-agentic/` prefix.
|
||||
|
||||
## Validation commands
|
||||
|
||||
The reproducible offline path requires no Devin account and has no runtime Internet:
|
||||
|
||||
```bash
|
||||
./scripts/devin-bridge/test-unit
|
||||
./scripts/devin-bridge/test-contract
|
||||
./scripts/devin-bridge/test-e2e-mock
|
||||
./scripts/devin-bridge/verify-anthropic-isolation
|
||||
```
|
||||
|
||||
The authenticated opt-in live path is:
|
||||
|
||||
```bash
|
||||
ENABLE_LIVE_DEVIN_TESTS=1 ./scripts/devin-bridge/test-live-devin
|
||||
```
|
||||
|
||||
The live runner waits between scenarios to avoid opening ACP sessions in a burst and
|
||||
validates structured Claude stream events instead of trusting textual claims. Its three
|
||||
scenarios prove:
|
||||
|
||||
1. direct project reads and defect analysis;
|
||||
2. a real `Edit`, a client-owned `Bash` `npm test`, and a terminal result;
|
||||
3. `/bridge-check` plus `bridge-proof` discovery, project reads, another successful
|
||||
client-owned `npm test`, and completion without pending work.
|
||||
|
||||
The final gate also checks the Devin network audit and requires the Claude egress audit to
|
||||
remain empty.
|
||||
|
||||
## Updating pinned tools
|
||||
|
||||
The image pins Node, Claude Code, and Devin CLI in
|
||||
`docker/devin-bridge/Dockerfile`. To update:
|
||||
|
||||
1. change the explicit versions;
|
||||
2. replace both architecture-specific Devin archive checksums with values for the official
|
||||
artifact;
|
||||
3. rebuild and run every offline validation command;
|
||||
4. confirm the versions inside the image;
|
||||
5. rerun the authenticated three-scenario live suite.
|
||||
|
||||
Do not install either CLI globally on the host or replace checksum verification with an
|
||||
unverified download.
|
||||
|
||||
## Diagnosis and cleanup
|
||||
|
||||
- `docker compose -f docker/devin-bridge/compose.yml --profile offline logs omniroute`
|
||||
shows local routing and sanitized executor errors.
|
||||
- `.sandbox/evidence/mock-acp.jsonl` records deterministic mock ACP actions.
|
||||
- `.sandbox/evidence/claude-stream.jsonl` records the real Claude Code offline run.
|
||||
- `.sandbox/evidence/live-*.jsonl` records the three validated live streams.
|
||||
- `.sandbox/evidence/egress.jsonl` and `.sandbox/evidence/claude-egress.jsonl` are validated,
|
||||
token-free copies of the guard audits.
|
||||
|
||||
Stop owned 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
|
||||
```
|
||||
|
||||
## Limits
|
||||
|
||||
- The bridge relies on the fixed no-tools `summarizer` role because Devin CLI `3000.2.17`
|
||||
does not expose a neutral no-tools ACP agent. The adapter compensates for summary-shaped
|
||||
intermediate responses, but one bounded repair can still fail explicitly.
|
||||
- Live ACP calls can return transient `502`/`504` responses. The harness spaces scenarios;
|
||||
persistent failure remains fail-closed and never selects another provider.
|
||||
- ACP context is reconstructed from each Anthropic request; there is no process/session
|
||||
affinity.
|
||||
- One tool call is supported per model response; parallel calls are rejected.
|
||||
- Images are explicitly unsupported. Vision, thinking output, effort controls, and a 1M
|
||||
context window are not advertised.
|
||||
- SSE uses valid Anthropic lifecycle events but is emitted after the bounded ACP turn is
|
||||
collected; ACP chunks are not forwarded incrementally.
|
||||
115
docs/DEVIN_CLAUDE_BRIDGE_PROGRESS.md
Normal file
115
docs/DEVIN_CLAUDE_BRIDGE_PROGRESS.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# Devin Claude Bridge Progress
|
||||
|
||||
Updated: 2026-07-28
|
||||
|
||||
## 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.
|
||||
|
||||
## Implemented architecture
|
||||
|
||||
- Claude Code runs only inside the non-root bridge container with its own empty config
|
||||
volume and local OmniRoute base URL.
|
||||
- `devin-cli-agentic` preserves Anthropic messages, tool schemas, `tool_use`, and
|
||||
`tool_result`, then calls the official Devin CLI over ACP stdio.
|
||||
- The executor starts `devin acp --agent-type summarizer`. This is the only fixed official
|
||||
ACP role in the pinned CLI that has no Devin-owned tools.
|
||||
- The request is framed as an execution trace. Devin can return one strict client tool
|
||||
envelope; Claude Code executes that tool locally.
|
||||
- Internal ACP `tool_call` events, unsupported blocks, invalid schemas, narrative actions,
|
||||
timeouts, cancellation, and process failure all fail closed.
|
||||
- Provider and network policy prevent combo/auto/Anthropic fallback.
|
||||
|
||||
## Offline proof
|
||||
|
||||
- Focused serializer, parser, executor, ACP lifecycle, wire-format, environment, and audit
|
||||
tests pass (39/39).
|
||||
- The contract suite covers Anthropic JSON/SSE, `tool_use`, `tool_result` continuation,
|
||||
fragmented ACP frames, stderr, early exit, timeout, cancellation, and fail-closed provider
|
||||
loss.
|
||||
- The production bridge image builds with the pinned CLIs.
|
||||
- Real Claude Code offline E2E loads `CLAUDE.md`, the project skill and slash command, fires
|
||||
hooks, executes local tools over multiple turns, observes a failed test, repairs the file,
|
||||
reruns the test, and completes.
|
||||
- The isolation verifier proves non-root/read-only execution, isolated mounts and config,
|
||||
blocked Anthropic/Claude access, no host credential mounts, local-only inference, and no
|
||||
fallback.
|
||||
|
||||
Evidence is generated under `.sandbox/evidence` and ignored by Git.
|
||||
|
||||
## Regression status
|
||||
|
||||
- `typecheck:core`, focused ESLint, Prettier, shell/Node syntax, and the complete documentation
|
||||
accuracy suite pass.
|
||||
- The broad `npm run check` is not reported as passed: after its lint phase, the repository
|
||||
test runner remained alive while an existing `ioredis` client repeatedly retried an
|
||||
unavailable local Redis endpoint after `quota-redis-store.test.ts`. The bridge-focused
|
||||
suites, production image build, offline E2E, isolation proof, and live gate do not use that
|
||||
Redis service and all pass.
|
||||
|
||||
## Live Devin proof
|
||||
|
||||
Passed with the official in-container login and discovered model
|
||||
`swe-1-7-lightning`. The terminal live run completed all three scenarios:
|
||||
|
||||
1. Claude Code loaded the fixture instructions, issued client-owned `Read` calls, and
|
||||
returned a correct defect analysis.
|
||||
2. Claude Code issued a real `Edit` changing subtraction to addition, then a client-owned
|
||||
`Bash` call running `npm test`; the test reported one pass and zero failures.
|
||||
3. Claude Code initialization listed `bridge-check` and `bridge-proof`, read the corrected
|
||||
source and test, executed another client-owned `npm test`, and completed successfully.
|
||||
|
||||
The live evidence validator parses stream JSON and requires successful tool results. It does
|
||||
not accept a textual claim that a tool ran. It also rejects terminal summaries that report a
|
||||
blocker, incomplete work, or required next steps.
|
||||
|
||||
The final live gate reported:
|
||||
|
||||
```text
|
||||
PASS: validated Claude evidence for LIVE_ANALYSIS_COMPLETE
|
||||
PASS: validated Claude evidence for LIVE_FIX_COMPLETE
|
||||
PASS: validated Claude evidence for BRIDGE_E2E_COMPLETE
|
||||
PASS: three live Devin-backed Claude Code scenarios completed
|
||||
PASS: live model swe-1-7-lightning was discovered and validated by three scenarios
|
||||
```
|
||||
|
||||
The same gate validated the network audit: only the Devin guard path was used, no internal
|
||||
Devin tool event was accepted, and the Claude egress audit remained empty.
|
||||
|
||||
## Investigation conclusion
|
||||
|
||||
The initial default-agent hypothesis failed because ACP permission modes do not turn the
|
||||
default Devin agent into a raw inference backend. Even `ask` mode can emit Devin-owned
|
||||
`tool_call` events. A discovered `allowed-tools: []` agent configuration was not consumed by
|
||||
`devin acp` in CLI `3000.2.17`.
|
||||
|
||||
The working adaptation uses the official `summarizer` agent because it is structurally
|
||||
no-tools. Its fixed summarization behavior can produce intermediate prose, so the bridge
|
||||
frames requests as execution traces, detects future-action narration, performs at most one
|
||||
strict repair, and otherwise fails. Live validation also exposed transient ACP timeouts;
|
||||
the harness now spaces independent scenarios rather than weakening routing or retrying into
|
||||
another provider.
|
||||
|
||||
## Safety record
|
||||
|
||||
No host Claude executable, configuration, login, OAuth token, Keychain, or Anthropic API was
|
||||
used. The dedicated Docker volumes remain role-separated. No credential value is written to
|
||||
the repository or evidence output.
|
||||
|
||||
During the early baseline, a focused test without isolated `DATA_DIR` initialized the
|
||||
repository's normal OmniRoute database at `/Users/lucasisrael/.omniroute/storage.sqlite`.
|
||||
It was not rolled back or touched again. Every bridge command now pins database and temporary
|
||||
paths under the worktree's `.sandbox` directory.
|
||||
|
||||
## Remaining limits
|
||||
|
||||
- The no-tools backend has a summarizer system role rather than a neutral generation role.
|
||||
- One client tool call per response is supported; parallel tool calls are rejected.
|
||||
- ACP processes are per-turn and stateless.
|
||||
- Live Devin availability can still produce explicit `502`/`504` failures.
|
||||
- Images and unadvertised vision/effort/large-context capabilities remain unsupported.
|
||||
@@ -381,6 +381,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`). |
|
||||
|
||||
252
docs/superpowers/plans/2026-07-27-devin-claude-bridge.md
Normal file
252
docs/superpowers/plans/2026-07-27-devin-claude-bridge.md
Normal file
@@ -0,0 +1,252 @@
|
||||
# Devin Claude Bridge Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Build a fail-closed `devin-cli-agentic` provider that serves local Anthropic Messages requests through Devin CLI ACP stdio while preserving Claude Code tool-use semantics.
|
||||
|
||||
**Architecture:** Add a separate Claude-format provider and executor instead of changing the existing OpenAI-format `devin-cli` summarizer. Keep parsing, prompt serialization, Anthropic response rendering, and ACP process handling in focused files under `open-sse/executors/devin-agentic/`, then wire them into the existing provider and executor registries.
|
||||
|
||||
**Tech Stack:** TypeScript ES modules, Node child process stdio, Anthropic Messages JSON/SSE, JSON-RPC 2.0 ACP, Node test runner.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Agentic Bridge Core
|
||||
|
||||
**Files:**
|
||||
- Create: `open-sse/executors/devin-agentic/types.ts`
|
||||
- Create: `open-sse/executors/devin-agentic/serializer.ts`
|
||||
- Create: `open-sse/executors/devin-agentic/toolParser.ts`
|
||||
- Create: `open-sse/executors/devin-agentic/anthropicResponse.ts`
|
||||
- Test: `tests/unit/executor-devin-cli-agentic-core.test.ts`
|
||||
|
||||
- [ ] **Implement and prove serialization, parsing, validation, and Anthropic rendering**
|
||||
|
||||
Interfaces:
|
||||
|
||||
```ts
|
||||
export function serializeAnthropicForDevin(body: unknown): DevinPrompt;
|
||||
export function parseDevinToolRequest(text: string, tools: AnthropicTool[]): ParsedToolRequest | null;
|
||||
export function buildClaudeTextResponse(args: ClaudeResponseArgs): Record<string, unknown>;
|
||||
export function buildClaudeToolUseResponse(args: ClaudeToolUseArgs): Record<string, unknown>;
|
||||
export function buildClaudeSseFrames(message: Record<string, unknown>): string;
|
||||
```
|
||||
|
||||
Invariants:
|
||||
|
||||
- Preserve `text`, `tool_use`, `tool_result`, `thinking`, and `redacted_thinking`.
|
||||
- Reject `image` with a clear error.
|
||||
- Reject unknown content block types.
|
||||
- Allow only one tool request per model turn.
|
||||
- Validate tool arguments against object JSON Schema with `required`, `type`, `properties`, `additionalProperties`, `enum`, `items`, and scalar types.
|
||||
- Generate deterministic ids from tool name and canonicalized arguments.
|
||||
|
||||
Run: `node --import tsx/esm --test tests/unit/executor-devin-cli-agentic-core.test.ts`
|
||||
Expected: core tests pass after dependencies are installed.
|
||||
|
||||
### Task 2: ACP Executor And Provider Wiring
|
||||
|
||||
**Files:**
|
||||
- Create: `open-sse/executors/devin-cli-agentic.ts`
|
||||
- Modify: `open-sse/executors/index.ts`
|
||||
- Create: `open-sse/config/providers/registry/devin-cli-agentic/index.ts`
|
||||
- Modify: `open-sse/config/providers/index.ts`
|
||||
- Test: `tests/unit/executor-devin-cli-agentic-acp.test.ts`
|
||||
|
||||
- [ ] **Implement and prove fail-closed ACP execution**
|
||||
|
||||
Behavior:
|
||||
|
||||
- `buildUrl()` returns `devin://acp/stdio`.
|
||||
- `buildHeaders()` returns `{}`.
|
||||
- `execute()` spawns only `devin acp` by default or the explicit `CLI_DEVIN_AGENTIC_BIN`/`CLI_DEVIN_BIN` override.
|
||||
- The child environment removes Anthropic and Claude routing credentials before spawn.
|
||||
- The executor sends `initialize`, `session/new`, and `session/prompt`.
|
||||
- The executor collects `agent_message_chunk` text and `session/prompt` final result.
|
||||
- Non-streaming Claude clients receive native Anthropic JSON.
|
||||
- Streaming Claude clients receive native Anthropic SSE lifecycle frames.
|
||||
- Spawn failure, ACP error, timeout, and early exit produce non-2xx responses with sanitized messages.
|
||||
|
||||
Run: `node --import tsx/esm --test tests/unit/executor-devin-cli-agentic-acp.test.ts`
|
||||
Expected: ACP mock tests pass after dependencies are installed.
|
||||
|
||||
### Task 3: Isolation Scripts And Documentation
|
||||
|
||||
**Files:**
|
||||
- Create: `scripts/devin-bridge/verify-anthropic-isolation`
|
||||
- Create: `scripts/devin-bridge/test-unit`
|
||||
- Create: `scripts/devin-bridge/launch`
|
||||
- Create: `docs/DEVIN_CLAUDE_BRIDGE.md`
|
||||
- Modify: `.gitignore`
|
||||
|
||||
- [ ] **Implement offline guardrails and operator docs**
|
||||
|
||||
Behavior:
|
||||
|
||||
- `verify-anthropic-isolation` fails if `CLAUDE_CONFIG_DIR` is missing, points outside an isolated path, or if Anthropic routing env vars are present.
|
||||
- `test-unit` runs the focused unit tests.
|
||||
- `launch` refuses to start unless `ENABLE_LIVE_DEVIN_TESTS=1` for live Devin or `DEVIN_BRIDGE_OFFLINE=1` for offline mock mode.
|
||||
- Documentation distinguishes tested offline behavior from live Devin opt-in behavior.
|
||||
|
||||
Run: `./scripts/devin-bridge/verify-anthropic-isolation` with explicit isolated env.
|
||||
Expected: exits 0 with isolated env and non-zero without it.
|
||||
|
||||
### Task 4: Verification
|
||||
|
||||
**Files:**
|
||||
- No additional source files.
|
||||
|
||||
- [ ] **Run proportional checks and capture real output**
|
||||
|
||||
Commands:
|
||||
|
||||
```bash
|
||||
./scripts/devin-bridge/test-unit
|
||||
npm test
|
||||
```
|
||||
|
||||
Expected in this workspace before installing dependencies: both commands fail with `ERR_MODULE_NOT_FOUND` for `tsx`. Expected after `npm install`: focused tests pass; `npm test` outcome must be reported from real output.
|
||||
|
||||
### Task 5: Close Core Security And Protocol Gaps
|
||||
|
||||
**Files:**
|
||||
- Modify: `open-sse/executors/devin-cli-agentic.ts`
|
||||
- Modify: `open-sse/executors/devin-agentic/*.ts`
|
||||
- Modify: `tests/unit/executor-devin-cli-agentic-*.test.ts`
|
||||
|
||||
- [ ] **Prove environment allowlisting, response-id correlation, strict standalone tool envelopes, unique ids, bounded repair, size limits, cancellation cleanup, sanitized errors, and explicit `devin://acp/stdio` validation**
|
||||
|
||||
Run with `HOME`, `DATA_DIR`, and `SQLITE_FILE` under `.sandbox`; expected: all focused tests pass and an outside-path test fails closed.
|
||||
|
||||
### Task 6: Build Reproducible Containers And Network Guard
|
||||
|
||||
**Files:**
|
||||
- Create: `docker/devin-bridge/Dockerfile`
|
||||
- Create: `docker/devin-bridge/compose.yml`
|
||||
- Create: `docker/devin-bridge/network-guard/*`
|
||||
- Create: `docker/devin-bridge/mock-devin/*`
|
||||
- Create: `.env.devin-bridge.example`
|
||||
|
||||
- [ ] **Pin Claude Code 2.1.220 and Devin CLI 3000.2.17, create non-root offline/live profiles, separate auth/config volumes, explicit env allowlist, no host credential mounts, and denied-domain telemetry**
|
||||
|
||||
Run: `docker compose -f docker/devin-bridge/compose.yml --profile offline config`; expected: no forbidden mounts/env inheritance and only internal runtime networks.
|
||||
|
||||
### Task 7: Deliver Isolation And Operator Scripts
|
||||
|
||||
**Files:**
|
||||
- Create/modify: `scripts/devin-bridge/{build,test-unit,test-contract,test-e2e-mock,verify-anthropic-isolation,login-devin,test-live-devin,launch,clean}`
|
||||
|
||||
- [ ] **Make every command idempotent, sandbox-scoped, fail-closed, and secret-safe**
|
||||
|
||||
Run: `./scripts/devin-bridge/verify-anthropic-isolation`; expected: positive offline proof passes and each deliberately removed guard returns non-zero.
|
||||
|
||||
### Task 8: Real Claude Code Offline E2E
|
||||
|
||||
**Files:**
|
||||
- Create: `tests/fixtures/devin-bridge/e2e-workspace/*`
|
||||
- Create: `tests/e2e/devin-claude-bridge.e2e.*`
|
||||
|
||||
- [ ] **Run pinned Claude Code in the offline container through local `/v1/messages` and mock ACP, proving CLAUDE.md, skill, command, hook, Read/Edit/Bash, tests, multi-turn continuation, and no Anthropic traffic**
|
||||
|
||||
Run: `./scripts/devin-bridge/test-e2e-mock`; expected: workspace diff and tests prove Claude Code executed tools while mock Devin only requested them.
|
||||
|
||||
### Task 9: Regression, Documentation, Live Gate, And Delivery
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/DEVIN_CLAUDE_BRIDGE.md`
|
||||
- Create: `docs/DEVIN_CLAUDE_BRIDGE_PROGRESS.md`
|
||||
|
||||
- [ ] **Run focused suites, typecheck, lint, build, docs checks, offline E2E, and isolation proof with fresh output; then run live only after official in-container Devin login**
|
||||
|
||||
If login is unavailable, record live as not tested and expose exactly `./scripts/devin-bridge/login-devin` followed by `./scripts/devin-bridge/test-live-devin`. Commit each reversible unit; do not merge or publish until all offline critical checks are green.
|
||||
|
||||
### Task 10: Close The Authenticated Live Runtime
|
||||
|
||||
**Files:**
|
||||
- Modify: `open-sse/executors/devin-cli-agentic.ts`
|
||||
- Modify: `docker/devin-bridge/compose.yml`
|
||||
- Create: `docker/devin-bridge/network-guard/policy.mjs`
|
||||
- Modify: `docker/devin-bridge/network-guard/proxy.mjs`
|
||||
- Modify: `scripts/devin-bridge/select-live-model.mjs`
|
||||
- Modify: `scripts/devin-bridge/common`
|
||||
- Modify: `scripts/devin-bridge/login-devin`
|
||||
- Modify: `scripts/devin-bridge/test-live-devin`
|
||||
- Modify: `scripts/devin-bridge/verify-anthropic-isolation`
|
||||
- Modify: `tests/unit/executor-devin-cli-agentic-acp.test.ts`
|
||||
- Create: `tests/unit/devin-bridge-live-runtime.test.ts`
|
||||
|
||||
- [ ] **Implement and prove the authenticated network, auth, and catalog boundaries with block-level TDD**
|
||||
|
||||
Invariants:
|
||||
|
||||
- The ACP child receives proxy variables only when `DEVIN_BRIDGE_PROXY_URL` is exactly
|
||||
`http://network-guard:8080`; arbitrary inherited proxy and credential variables stay absent.
|
||||
- The guard permits suffixes `.devin.ai` and `.cognition.ai`, exact hosts
|
||||
`server.codeium.com` and `unleash.codeium.com`, and nothing else.
|
||||
- Claude services cannot mount `devin-auth`; non-Claude services cannot mount the Claude config.
|
||||
- A zero exit from `devin auth status` is insufficient when output contains a server-fetch failure.
|
||||
- `family_uid: swe-1.7-lightning` resolves to catalog id `swe-1-7-lightning`; unknown normalized
|
||||
values fail instead of becoming model ids.
|
||||
- Login uses the official manual-token flow so no container loopback callback is required.
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
./scripts/devin-bridge/test-unit
|
||||
node --import tsx/esm --test tests/unit/devin-bridge-live-runtime.test.ts
|
||||
./scripts/devin-bridge/verify-anthropic-isolation --static
|
||||
```
|
||||
|
||||
Expected: focused tests and static isolation pass; deliberate untrusted proxy, host, mount, auth
|
||||
status, and model fixtures fail closed.
|
||||
|
||||
- [ ] **Commit the reversible live-runtime repair**
|
||||
|
||||
```bash
|
||||
git add open-sse/executors/devin-cli-agentic.ts docker/devin-bridge \
|
||||
scripts/devin-bridge tests/unit/devin-bridge-live-runtime.test.ts \
|
||||
tests/unit/executor-devin-cli-agentic-acp.test.ts
|
||||
git commit -m "fix: close Devin bridge live runtime gaps"
|
||||
```
|
||||
|
||||
### Task 11: Prove Offline And Live Completion
|
||||
|
||||
**Files:**
|
||||
- Modify: `docker/devin-bridge/run-claude-live-e2e.sh`
|
||||
- Modify: `docs/DEVIN_CLAUDE_BRIDGE.md`
|
||||
- Modify: `docs/DEVIN_CLAUDE_BRIDGE_PROGRESS.md`
|
||||
|
||||
- [ ] **Run the complete deterministic bridge proof before any paid request**
|
||||
|
||||
```bash
|
||||
./scripts/devin-bridge/test-unit
|
||||
./scripts/devin-bridge/test-contract
|
||||
./scripts/devin-bridge/test-e2e-mock
|
||||
./scripts/devin-bridge/verify-anthropic-isolation
|
||||
npm run typecheck:core
|
||||
npm run lint
|
||||
npm run build
|
||||
npm run check:docs-all
|
||||
```
|
||||
|
||||
Expected: all bridge-specific checks, typecheck, lint, build, and documentation checks pass with
|
||||
isolated data paths. Any unrelated full-suite infrastructure hang is recorded separately and is
|
||||
not converted into a pass.
|
||||
|
||||
- [ ] **Run exactly the three authorized live scenarios and the no-fallback failure probe**
|
||||
|
||||
```bash
|
||||
ENABLE_LIVE_DEVIN_TESTS=1 ./scripts/devin-bridge/test-live-devin
|
||||
```
|
||||
|
||||
Expected: dynamic discovery selects a returned Devin catalog model; Claude Code reads without
|
||||
editing, then edits and runs the fixture test, then executes the fixture command. Evidence shows
|
||||
native tool use by Claude Code, only `devin-cli-agentic` routing, no allowed non-Devin egress,
|
||||
and an Anthropic-shaped error after the Devin backend is deliberately made unavailable.
|
||||
|
||||
- [ ] **Update verified documentation and commit the evidence-backed delivery state**
|
||||
|
||||
```bash
|
||||
git add docker/devin-bridge/run-claude-live-e2e.sh docs/DEVIN_CLAUDE_BRIDGE.md \
|
||||
docs/DEVIN_CLAUDE_BRIDGE_PROGRESS.md
|
||||
git commit -m "docs: record verified Devin bridge live delivery"
|
||||
```
|
||||
134
docs/superpowers/specs/2026-07-27-devin-claude-bridge-design.md
Normal file
134
docs/superpowers/specs/2026-07-27-devin-claude-bridge-design.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# Devin Claude Bridge Design
|
||||
|
||||
## Baseline
|
||||
|
||||
- Branch: `release/v3.8.49`
|
||||
- HEAD: `ed7db3ee5f89a144b2d931d8605534522f83de30`
|
||||
- Package version: `3.8.49`
|
||||
- Node: `v26.0.0`
|
||||
- npm: `11.12.1`
|
||||
- Pre-existing worktree state: `.tug/` untracked
|
||||
- Dependency state: `node_modules` is absent; the first focused test run failed before loading tests because `tsx` was not installed.
|
||||
- Tugline state: `tug` exists, but `tug search` failed with MCP connection closed and `tug doctor` hung; it was interrupted.
|
||||
- Upstream check: `git ls-remote` failed because GitHub DNS was unavailable. Web search of the public repository showed the existing `devin-cli` summarizer provider, but no evidence of `devin-cli-agentic`.
|
||||
|
||||
## Source Anchors
|
||||
|
||||
- `/v1/messages`: `src/app/api/v1/messages/route.ts`
|
||||
- Existing Devin provider: `open-sse/config/providers/registry/devin-cli/index.ts`
|
||||
- Existing Devin executor: `open-sse/executors/devin-cli.ts`
|
||||
- Executor registry: `open-sse/executors/index.ts`
|
||||
- Provider registry: `open-sse/config/providers/index.ts`
|
||||
- Format detection: `open-sse/services/provider.ts`
|
||||
- Claude non-streaming response conversion: `open-sse/handlers/responseTranslator.ts`
|
||||
- Existing Devin ACP unit test: `tests/unit/executor-devin-cli-acp-protocol-8406.test.ts`
|
||||
|
||||
## Findings
|
||||
|
||||
The existing `devin-cli` provider is intentionally OpenAI-format and summarizer-oriented. Its executor spawns `devin acp --agent-type summarizer`, flattens the message history into a single text prompt, and emits OpenAI SSE text chunks. It does not preserve Anthropic `tool_use` and `tool_result` blocks.
|
||||
|
||||
The safest implementation is a new provider id, `devin-cli-agentic`, with a separate executor. This leaves `devin-cli`, Anthropic OAuth, Claude OAuth, Claude Web, and all host Claude configuration code untouched. The new provider is fail-closed: it only resolves to `devin://acp/stdio`, uses the official Devin CLI ACP stdio path, and has no fallback provider.
|
||||
|
||||
## Architecture
|
||||
|
||||
Claude Code sends Anthropic Messages requests to local OmniRoute. OmniRoute resolves model ids prefixed with `devin-cli-agentic/` to a new Claude-format provider. The new executor translates the complete Anthropic request into an explicit text prompt for Devin ACP, including system text, structured message history, tool schemas, and prior tool results.
|
||||
|
||||
Devin remains a model backend. The executor starts the official fixed no-tools summarizer
|
||||
role with `devin acp --agent-type summarizer` and frames the serialized request as an
|
||||
execution trace. Devin must request client-owned tool execution by emitting a strict
|
||||
XML-wrapped JSON block:
|
||||
|
||||
```xml
|
||||
<tool>
|
||||
{"name":"Read","arguments":{"file_path":"src/index.ts"}}
|
||||
</tool>
|
||||
```
|
||||
|
||||
The bridge parses exactly one tool request per model turn, validates that the tool name was supplied in the incoming request, validates arguments against a minimal JSON Schema validator, generates a stable `tool_devin_...` id, and returns a native Anthropic `tool_use` block. If no valid tool request is present, the bridge returns text with `stop_reason: "end_turn"`.
|
||||
|
||||
## Error And Safety Rules
|
||||
|
||||
- Unsupported Anthropic content blocks fail explicitly; images are rejected.
|
||||
- Unknown tools fail explicitly.
|
||||
- Invalid tool arguments fail explicitly.
|
||||
- Invalid tool XML/JSON fails explicitly.
|
||||
- Narrative claims that a tool was executed are returned as text, not actions.
|
||||
- ACP spawn, timeout, early exit, and stderr-only failures return explicit Devin errors.
|
||||
- The executor never reads `~/.claude`, `~/.claude.json`, macOS Keychain paths, or host Claude config.
|
||||
- Live Devin is outside normal tests and remains opt-in via `ENABLE_LIVE_DEVIN_TESTS=1`.
|
||||
|
||||
## Test Strategy
|
||||
|
||||
Focused unit tests cover serialization, tool parsing, validation, Anthropic JSON, Anthropic SSE, malformed tool output, unknown tools, invalid arguments, image rejection, timeout, and spawn failure. Environment scripts provide an offline isolation verifier without reading host Claude credentials.
|
||||
|
||||
## 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 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` 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
|
||||
|
||||
`devin-cli-agentic` accepts only the synthetic `devin://acp/stdio` target and an explicit Devin binary path inside the container. It cannot use provider combos, auto routing, account fallback, fallback URLs, or an HTTP upstream. Model aliases resolve only to models returned by the Devin catalog or explicitly configured Devin model ids. An ACP failure, timeout, cancellation, invalid frame, unavailable model, or stopped sidecar becomes an Anthropic-shaped error response; no secondary provider is attempted.
|
||||
|
||||
## Agentic Contract
|
||||
|
||||
The serializer preserves request order, `system`, `tool_choice`, exact tool schemas, `text`, `tool_use`, `tool_result`, `thinking`, and `redacted_thinking`. It rejects unsupported blocks and caps large tool results with an explicit truncation marker and original size. The parser accepts exactly one standalone `<tool>` envelope, validates with Zod/JSON Schema infrastructure already present in OmniRoute, rejects unknown tools and mixed narrative/action output, and performs at most one bounded repair prompt. Tool ids combine a per-request nonce with canonical arguments so repeated identical calls remain unique while their association is stable within the turn.
|
||||
|
||||
## Required Proof
|
||||
|
||||
The offline profile must prove the ACP lifecycle, fragmented frames, stderr, early exit, hang/cancel, Anthropic JSON/SSE order, no fallback, and a real pinned Claude Code run that reads, edits, runs tests, observes `CLAUDE.md`, loads a skill and command, fires a hook, and completes at least one `tool_use -> tool_result -> continuation` loop. The isolation verifier checks env, mounts, UID, config paths, DNS/connection logs, local inference destination, selected provider, and fail-closed behavior. Live Devin is proved only by official in-container login and three isolated agentic scenarios.
|
||||
|
||||
## Safety Incident During Baseline
|
||||
|
||||
The first focused test was run without `DATA_DIR` isolation and initialized `/Users/lucasisrael/.omniroute/storage.sqlite`; logs reported schema-column additions. No Anthropic data was accessed. The external database will not be touched again or destructively rolled back. Every bridge command and test now must set `HOME`, `DATA_DIR`, `SQLITE_FILE`, and temporary directories inside `.sandbox`, and an automated guard must reject paths outside the task workspace.
|
||||
|
||||
## Live Completion Repair
|
||||
|
||||
The first authenticated live attempt disproved four assumptions in the initial container
|
||||
design. The official CLI reports a valid login even when its server-status request fails;
|
||||
that request uses the exact hosts `server.codeium.com` and `unleash.codeium.com`, which the
|
||||
guard denied. The OmniRoute executor also built a fresh allowlisted child environment that
|
||||
omitted the proxy, so `devin acp` could not leave the internal network. Model discovery emits
|
||||
family identifiers such as `swe-1.7`, while the OmniRoute catalog uses canonical ids such as
|
||||
`swe-1-7`. Finally, browser login redirects to a loopback listener inside the one-off
|
||||
container, which is not reachable from the host browser.
|
||||
|
||||
The repair keeps the fully containerized architecture and does not weaken the deny-by-default
|
||||
network. The guard gains an exact-host allowlist for the two Codeium control-plane hosts while
|
||||
retaining suffix-based access only for Devin and Cognition; telemetry destinations such as
|
||||
Sentry remain denied. Compose supplies `DEVIN_BRIDGE_PROXY_URL` with the single accepted value
|
||||
`http://network-guard:8080`, and the executor derives `HTTP_PROXY` and `HTTPS_PROXY` from that
|
||||
explicit bridge setting instead of inheriting arbitrary host proxy variables. Claude services
|
||||
mount only the Claude config volume, and only the OmniRoute live service mounts the Devin auth
|
||||
volume.
|
||||
|
||||
Fresh login uses the official `devin auth login --force-manual-token-flow`, which is intended
|
||||
for remote environments where localhost redirects cannot work. The credential is pasted only
|
||||
into the interactive CLI terminal and never appears in arguments, logs, evidence, or Git.
|
||||
Authentication validation requires both the logged-in marker and the absence of a server-fetch
|
||||
failure. Model discovery accepts the real `family_uid`/`model_uid` fields, maps punctuation to a
|
||||
catalog id only after an exact normalized match, and prefers the already-proved lightning model
|
||||
when available.
|
||||
|
||||
Tests first prove the trusted proxy boundary, exact host policy, volume separation, strict auth
|
||||
status gate, and catalog normalization. The live gate then runs three real Claude Code scenarios
|
||||
through the authenticated in-container Devin CLI and requires local Read/Edit/Bash activity,
|
||||
passing fixture tests, Devin-only routing, no allowed non-Devin egress, and an explicit error
|
||||
when the Devin backend is stopped.
|
||||
|
||||
## Final Live Result
|
||||
|
||||
The default-agent design was rejected after live evidence showed that `ask` mode can still
|
||||
emit Devin-owned ACP tool calls. The pinned CLI does not apply its top-level agent
|
||||
configuration to `devin acp`, so an `allowed-tools: []` configuration could not create a
|
||||
neutral backend. The fixed summarizer role is the only official ACP role in this version that
|
||||
is structurally no-tools.
|
||||
|
||||
The execution-trace adaptation passed the authenticated live gate with
|
||||
`swe-1-7-lightning`. Three Claude Code processes completed analysis, edit/test, and local
|
||||
command/skill scenarios. Structured evidence proved that Claude Code issued `Read`, `Edit`,
|
||||
and `Bash` tool calls; two client-owned `npm test` calls succeeded. The guard audit proved
|
||||
Devin-only outbound access and zero Claude egress. Intermediate summary-shaped responses and
|
||||
transient ACP timeouts remain explicit failure modes; the adapter performs one bounded repair
|
||||
and the harness spaces scenarios to avoid bursty session creation.
|
||||
Reference in New Issue
Block a user