Skip to content

feat(acp): browser control via MCP-over-ACP + stdio bridge (Phase 2) #1789

feat(acp): browser control via MCP-over-ACP + stdio bridge (Phase 2)

feat(acp): browser control via MCP-over-ACP + stdio bridge (Phase 2) #1789

name: Docker Smoke Test
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths:
- 'Dockerfile*'
- 'src/**'
- 'crates/**'
- 'Cargo.*'
concurrency:
group: smoke-test-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
smoke-test:
if: github.event.pull_request.draft == false
strategy:
fail-fast: false
matrix:
variant:
- { dockerfile: Dockerfile, suffix: "", agent: "kiro-cli", agent_args: "acp --trust-all-tools", build_args: "" }
- { dockerfile: Dockerfile, suffix: "-unified", agent: "kiro-cli", agent_args: "acp --trust-all-tools", build_args: "--build-arg BUILD_MODE=unified" }
- { dockerfile: Dockerfile.claude, suffix: "-claude", agent: "claude-agent-acp", agent_args: "", build_args: "" }
- { dockerfile: Dockerfile.codex, suffix: "-codex", agent: "codex-acp", agent_args: "", build_args: "" }
- { dockerfile: Dockerfile.gemini, suffix: "-gemini", agent: "gemini", agent_args: "--acp", build_args: "" }
- { dockerfile: Dockerfile.copilot, suffix: "-copilot", agent: "copilot", agent_args: "--acp", build_args: "" }
- { dockerfile: Dockerfile.opencode, suffix: "-opencode", agent: "opencode", agent_args: "acp", build_args: "" }
- { dockerfile: Dockerfile.cursor, suffix: "-cursor", agent: "cursor-agent", agent_args: "acp", build_args: "" }
- { dockerfile: Dockerfile.kimi, suffix: "-kimi", agent: "kimi", agent_args: "acp", build_args: "" }
- { dockerfile: Dockerfile.mimocode, suffix: "-mimocode", agent: "mimo", agent_args: "acp", build_args: "" }
- { dockerfile: Dockerfile.hermes, suffix: "-hermes", agent: "hermes-acp", agent_args: "", build_args: "" }
- { dockerfile: Dockerfile.grok, suffix: "-grok", agent: "grok", agent_args: "agent stdio", build_args: "" }
- { dockerfile: Dockerfile.devin, suffix: "-devin", agent: "devin", agent_args: "acp", build_args: "" }
- { dockerfile: Dockerfile.antigravity, suffix: "-antigravity", agent: "agy-acp", agent_args: "", build_args: "" }
- { dockerfile: Dockerfile.pi, suffix: "-pi", agent: "pi-acp", agent_args: "", build_args: "" }
- { dockerfile: openshell/Dockerfile, suffix: "-native-sandbox", agent: "openab-agent", agent_args: "", build_args: "" }
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Build image
run: docker build ${{ matrix.variant.build_args }} -t openab-test${{ matrix.variant.suffix }} -f ${{ matrix.variant.dockerfile }} .
- name: Verify openab CMD does not crash
run: |
OUTPUT=$(docker run --rm openab-test${{ matrix.variant.suffix }} 2>&1 || true)
if echo "$OUTPUT" | grep -q "unrecognized subcommand"; then
echo "❌ CMD regression: $OUTPUT"
exit 1
fi
echo "✅ openab CMD ok"
- name: Verify agent CLI exists
run: docker run --rm --entrypoint which openab-test${{ matrix.variant.suffix }} ${{ matrix.variant.agent }}
- name: Verify Codex runtime versions
if: matrix.variant.suffix == '-codex'
run: |
# Expected versions come from the Dockerfile ARG defaults (single
# source of truth) so a version bump only touches the Dockerfiles.
CODEX_ACP_VERSION=$(awk -F= '$1 == "ARG CODEX_ACP_VERSION" {print $2; exit}' Dockerfile.codex)
CODEX_VERSION=$(awk -F= '$1 == "ARG CODEX_VERSION" {print $2; exit}' Dockerfile.codex)
test -n "$CODEX_ACP_VERSION" && test -n "$CODEX_VERSION"
docker run --rm --entrypoint codex-acp openab-test-codex --version \
| grep -F "@agentclientprotocol/codex-acp ${CODEX_ACP_VERSION}"
docker run --rm --entrypoint codex openab-test-codex --version \
| grep -F "codex-cli ${CODEX_VERSION}"
- name: Verify Codex agent command is a single token
if: matrix.variant.suffix == '-codex'
run: |
# Regression guard: OPENAB_AGENT_COMMAND must stay a plain binary.
# Packing env-var prefixes (e.g. "/usr/bin/env K=V codex-acp") breaks
# configs that override [agent].args only — OAB keeps token 1 as the
# command and replaces the rest, yielding "/usr/bin/env <user args>".
docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' openab-test-codex \
| grep -Fx 'OPENAB_AGENT_COMMAND=codex-acp'
- name: Verify bumped runtime versions
if: matrix.variant.suffix == '' || matrix.variant.suffix == '-unified' || matrix.variant.suffix == '-copilot' || matrix.variant.suffix == '-devin' || matrix.variant.suffix == '-antigravity'
shell: bash
run: |
set -euo pipefail
case "${{ matrix.variant.suffix }}" in
''|-unified)
expected=$(awk -F= '$1 == "ARG KIRO_CLI_VERSION" {print $2; exit}' Dockerfile)
test -n "$expected" || { echo "❌ Failed to extract Kiro expected version"; exit 1; }
docker run --rm --entrypoint kiro-cli openab-test${{ matrix.variant.suffix }} --version \
| grep -Fx "kiro-cli $expected"
;;
-copilot)
expected=$(awk -F= '$1 == "ARG COPILOT_VERSION" {print $2; exit}' Dockerfile.copilot)
test -n "$expected" || { echo "❌ Failed to extract Copilot expected version"; exit 1; }
docker run --rm --entrypoint sh openab-test-copilot \
-c 'NODE_PATH=$(npm root -g) node -e "process.stdout.write(require(\"@github/copilot/package.json\").version)"' \
| grep -Fx "$expected"
;;
-devin)
expected=$(awk -F= '$1 == "ARG DEVIN_VERSION" {print $2; exit}' Dockerfile.devin)
test -n "$expected" || { echo "❌ Failed to extract Devin expected version"; exit 1; }
docker run --rm --entrypoint devin openab-test-devin --version \
| grep -F "$expected"
;;
-antigravity)
expected=$(awk -F= '$1 == "ENV AGY_VERSION" {print $2; exit}' Dockerfile.antigravity)
test -n "$expected" || { echo "❌ Failed to extract Antigravity expected version"; exit 1; }
docker run --rm --entrypoint agy openab-test-antigravity --version \
| grep -F "$expected"
;;
esac
- name: Verify agent responds
run: |
if [ "${{ matrix.variant.suffix }}" = "-codex" ]; then
INIT='{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":1,"clientCapabilities":{},"clientInfo":{"name":"ci-test","version":"0.0.1"}}}'
else
INIT='{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","clientCapabilities":{},"clientInfo":{"name":"ci-test","version":"0.0.1"}}}'
fi
# Start agent in background, send init, capture output with timeout
CID=$(docker run -d -i --entrypoint sh openab-test${{ matrix.variant.suffix }} -c 'exec ${{ matrix.variant.agent }} ${{ matrix.variant.agent_args }} 2>/dev/null')
echo "$INIT" | docker attach --no-stdin=false "$CID" &
sleep 5
RESPONSE=$(docker logs "$CID" 2>/dev/null | grep -m1 '^{' || true)
docker rm -f "$CID" >/dev/null 2>&1
echo "Response: $RESPONSE"
if [ -n "$RESPONSE" ] && echo "$RESPONSE" | jq -e '.result.agentInfo.name' > /dev/null 2>&1; then
AGENT_NAME=$(echo "$RESPONSE" | jq -r '.result.agentInfo.name')
echo "✅ ACP handshake ok — agent=$AGENT_NAME"
elif [ "${{ matrix.variant.suffix }}" = "-codex" ]; then
echo "❌ Codex ACP handshake failed"
exit 1
else
echo "⚠️ ACP handshake returned no response — falling back to CLI check"
docker run --rm --entrypoint ${{ matrix.variant.agent }} \
openab-test${{ matrix.variant.suffix }} --help >/dev/null 2>&1
echo "✅ Agent CLI responds (ACP skipped — likely needs auth)"
fi
validate-packaged-pins:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Validate package and legacy pin parity
shell: bash
run: |
set -euo pipefail
assert_line() {
local expected="$1"
shift
for file in "$@"; do
grep -Fx "$expected" "$file"
done
}
assert_line 'ARG KIRO_CLI_VERSION=2.13.0' Dockerfile Dockerfile.package Dockerfile.unified
assert_line 'ARG KIRO_SHA256_AMD64=d8c5277358b4a82b2d9a9ed2d52e110862536dc82b9e32c3719fc5f5a9834c94' Dockerfile Dockerfile.package Dockerfile.unified
assert_line 'ARG KIRO_SHA256_ARM64=95972602568c2065b7d8cc28924730304d40e612c0984ee0144d8ba452000be3' Dockerfile Dockerfile.package Dockerfile.unified
assert_line 'ARG CODEX_ACP_VERSION=1.1.4' Dockerfile.codex Dockerfile.package Dockerfile.unified
assert_line 'ARG CODEX_VERSION=0.144.1' Dockerfile.codex Dockerfile.package Dockerfile.unified
assert_line 'ARG COPILOT_VERSION=1.0.71' Dockerfile.copilot Dockerfile.package Dockerfile.unified
assert_line 'ARG DEVIN_VERSION=3000.2.17' Dockerfile.devin Dockerfile.package Dockerfile.unified
assert_line 'ARG DEVIN_SHA256_AMD64=f0e1e9363afc6ee68c4ef87bab4aeb7ff5cc08a5fa838350ef3ceefdbb2a2be2' Dockerfile.devin Dockerfile.package Dockerfile.unified
assert_line 'ARG DEVIN_SHA256_ARM64=116dc71ef085a922bc3ff0ea0377d4b26c529a431d58246e36572913e2d25624' Dockerfile.devin Dockerfile.package Dockerfile.unified
assert_line 'ENV AGY_VERSION=1.1.4' Dockerfile.antigravity Dockerfile.package Dockerfile.unified Dockerfile.final Dockerfile.ci
assert_line 'ARG AGY_SHA256_AMD64=aaab42e395cb4e3bfe5ae88994a340865d949f7a9e7f0604ffa2a3f1e8aadbfa' Dockerfile.antigravity Dockerfile.package Dockerfile.unified Dockerfile.final Dockerfile.ci
assert_line 'ARG AGY_SHA256_ARM64=333199327cc29e21cb8fcc4e19f7c02c462bf41dd876f1328d64e9922c631700' Dockerfile.antigravity Dockerfile.package Dockerfile.unified Dockerfile.final Dockerfile.ci