diff --git a/.gitea/workflows/continuous-synth-e2e.yml b/.gitea/workflows/continuous-synth-e2e.yml index 569a11197..e26c8577e 100644 --- a/.gitea/workflows/continuous-synth-e2e.yml +++ b/.gitea/workflows/continuous-synth-e2e.yml @@ -166,6 +166,10 @@ jobs: # canary path. The script picks the right blob shape based on # which key is non-empty. E2E_OPENAI_API_KEY: ${{ secrets.MOLECULE_STAGING_OPENAI_API_KEY }} + # google-adk canary path — AI-Studio key (config model + # google_genai:gemini-2.5-pro). PROD disallows API keys (Vertex+ADC); + # the keyed path is CI-only. Dispatch with E2E_RUNTIME=google-adk. + E2E_GOOGLE_API_KEY: ${{ secrets.MOLECULE_STAGING_GOOGLE_API_KEY }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -217,6 +221,10 @@ jobs: required_secret_name="MOLECULE_STAGING_OPENAI_API_KEY" required_secret_value="${E2E_OPENAI_API_KEY:-}" ;; + google-adk) + required_secret_name="MOLECULE_STAGING_GOOGLE_API_KEY" + required_secret_value="${E2E_GOOGLE_API_KEY:-}" + ;; *) echo "::warning::Unknown E2E_RUNTIME='${E2E_RUNTIME}' — skipping LLM-key check" required_secret_name="" diff --git a/.gitea/workflows/e2e-staging-saas.yml b/.gitea/workflows/e2e-staging-saas.yml index 92db50363..56202e98d 100644 --- a/.gitea/workflows/e2e-staging-saas.yml +++ b/.gitea/workflows/e2e-staging-saas.yml @@ -157,13 +157,18 @@ jobs: # E2E_RUNTIME=hermes or =codex via workflow_dispatch can still # exercise the OpenAI path. E2E_OPENAI_API_KEY: ${{ secrets.MOLECULE_STAGING_OPENAI_API_KEY }} + # google-adk (operator-dispatched only) auths Gemini with an + # AI-Studio key. Org policy disallows API keys in PROD (Vertex+ADC + # there); CI uses the keyed AI-Studio path with config model + # google_genai:gemini-2.5-pro. Vertex remains the supported prod path. + E2E_GOOGLE_API_KEY: ${{ secrets.MOLECULE_STAGING_GOOGLE_API_KEY }} E2E_RUNTIME: ${{ github.event.inputs.runtime || 'claude-code' }} # Pin the model when running on the default claude-code path — # the per-runtime default ("sonnet") routes to direct Anthropic # and defeats the cost saving. Operators can override via the # workflow_dispatch flow (no input wired here yet — runtime # override is enough for ad-hoc). - E2E_MODEL_SLUG: ${{ github.event.inputs.runtime == 'hermes' && 'openai/gpt-4o' || github.event.inputs.runtime == 'codex' && 'openai/gpt-4o' || 'MiniMax-M2' }} + E2E_MODEL_SLUG: ${{ github.event.inputs.runtime == 'hermes' && 'openai/gpt-4o' || github.event.inputs.runtime == 'codex' && 'openai/gpt-4o' || github.event.inputs.runtime == 'google-adk' && 'google_genai:gemini-2.5-pro' || 'MiniMax-M2' }} E2E_RUN_ID: "${{ github.run_id }}-${{ github.run_attempt }}" E2E_KEEP_ORG: ${{ github.event.inputs.keep_org && '1' || '0' }} @@ -212,6 +217,10 @@ jobs: required_secret_name="MOLECULE_STAGING_OPENAI_API_KEY" required_secret_value="${E2E_OPENAI_API_KEY:-}" ;; + google-adk) + required_secret_name="MOLECULE_STAGING_GOOGLE_API_KEY" + required_secret_value="${E2E_GOOGLE_API_KEY:-}" + ;; *) echo "::warning::Unknown E2E_RUNTIME='${E2E_RUNTIME}' — skipping LLM-key check" required_secret_name="" diff --git a/scripts/test-all-runtimes-a2a-e2e.sh b/scripts/test-all-runtimes-a2a-e2e.sh index 013ceead3..67292eb05 100755 --- a/scripts/test-all-runtimes-a2a-e2e.sh +++ b/scripts/test-all-runtimes-a2a-e2e.sh @@ -1,12 +1,13 @@ #!/usr/bin/env bash -# E2E test: A2A round-trip parity across all four runtimes. +# E2E test: A2A round-trip parity across all five runtimes. # -# Validates that for each of {claude-code, hermes, codex, openclaw}: +# Validates that for each of {claude-code, hermes, codex, openclaw, google-adk}: # 1. A workspace can be provisioned + brought online # 2. The adapter responds to A2A message/send # 3. The reply contains expected content (echo of the prompt) # 4. A SECOND message preserves session state where the runtime -# supports it (currently: hermes via plugin path) +# supports it (currently: hermes via plugin path; google-adk via +# ADK InMemorySessionService keyed on A2A context_id) # # Targets a SaaS tenant subdomain. Provisions workspaces in the calling # tenant, runs the round-trip, deletes them on success. @@ -16,6 +17,10 @@ # (e.g. https://demo-tenant.staging.moleculesai.app) # - $OPENROUTER_API_KEY (or $HERMES_API_KEY) for non-claude runtimes # - $OPENAI_API_KEY for claude-code peer +# - $GOOGLE_API_KEY (AI Studio) for google-adk — the org disallows API +# keys in PROD (Vertex+ADC there), but CI auths Gemini with an +# AI-Studio key (config model google_genai:gemini-2.5-pro). Vertex +# stays supported; this is the keyed CI path only. # - SaaS edge requires Origin header — see auto-memory # reference_saas_waf_origin_header.md # @@ -24,12 +29,13 @@ # ./scripts/test-all-runtimes-a2a-e2e.sh # # Skip individual runtimes: -# SKIP_HERMES=1 SKIP_OPENCLAW=1 ./scripts/test-all-runtimes-a2a-e2e.sh +# SKIP_HERMES=1 SKIP_OPENCLAW=1 SKIP_GOOGLE_ADK=1 ./scripts/test-all-runtimes-a2a-e2e.sh set -euo pipefail PLATFORM="${PLATFORM:-${1:-http://localhost:8080}}" HERMES_PROVIDER_KEY="${OPENROUTER_API_KEY:-${HERMES_API_KEY:-}}" PEER_OPENAI_KEY="${OPENAI_API_KEY:-}" +GOOGLE_ADK_KEY="${GOOGLE_API_KEY:-}" # SaaS auth chain — TENANT_ADMIN_TOKEN + TENANT_ORG_ID required when # hitting *.moleculesai.app (per-tenant ADMIN_TOKEN, NOT # CP_ADMIN_API_TOKEN). Optional for localhost. @@ -48,6 +54,10 @@ if [ -z "$HERMES_PROVIDER_KEY" ] && [ -z "${SKIP_HERMES:-}${SKIP_CODEX:-}${SKIP_ echo "FAIL: set OPENROUTER_API_KEY or HERMES_API_KEY for non-claude runtimes" exit 2 fi +if [ -z "$GOOGLE_ADK_KEY" ] && [ -z "${SKIP_GOOGLE_ADK:-}" ]; then + echo "FAIL: set GOOGLE_API_KEY (AI Studio) for google-adk, or SKIP_GOOGLE_ADK=1" + exit 2 +fi PASS=0 FAIL=0 @@ -143,7 +153,7 @@ echo "==========================================" echo "" # ------------------------------------------------------- -# 1. Provision the four runtimes (skip via SKIP_* flags) +# 1. Provision the five runtimes (skip via SKIP_* flags) # ------------------------------------------------------- echo "--- 1. Provision workspaces ---" if [ -z "${SKIP_CLAUDE_CODE:-}" ]; then @@ -162,6 +172,10 @@ if [ -z "${SKIP_OPENCLAW:-}" ]; then WS_IDS[openclaw]=$(provision "ParityOpenClaw" "openclaw" "openclaw peer") echo " openclaw: ${WS_IDS[openclaw]}" fi +if [ -z "${SKIP_GOOGLE_ADK:-}" ]; then + WS_IDS[google-adk]=$(provision "ParityGoogleADK" "google-adk" "google-adk peer") + echo " google-adk: ${WS_IDS[google-adk]}" +fi # ------------------------------------------------------- # 2. Set provider keys @@ -177,6 +191,12 @@ if [ -n "${WS_IDS[claude-code]:-}" ] && [ -n "$PEER_OPENAI_KEY" ]; then set_secret "${WS_IDS[claude-code]}" "OPENAI_API_KEY" "$PEER_OPENAI_KEY" echo " claude-code: OPENAI_API_KEY set" fi +if [ -n "${WS_IDS[google-adk]:-}" ] && [ -n "$GOOGLE_ADK_KEY" ]; then + # AI-Studio path: the adapter reads GOOGLE_API_KEY natively when the + # config model is google_genai:gemini-2.5-pro (see _routing.resolve_model). + set_secret "${WS_IDS[google-adk]}" "GOOGLE_API_KEY" "$GOOGLE_ADK_KEY" + echo " google-adk: GOOGLE_API_KEY set" +fi # ------------------------------------------------------- # 3. Wait for online @@ -188,6 +208,9 @@ for runtime in "${!WS_IDS[@]}"; do [ -z "$id" ] && continue max=60 [ "$runtime" = "hermes" ] && max=120 + # google-adk's first cold boot pulls a large fresh ADK image — give it + # a hermes-class window so a slow first pull doesn't read as "failed". + [ "$runtime" = "google-adk" ] && max=180 if wait_online "$id" "$runtime" "$max"; then check "$runtime online" "ok" "ok" else @@ -200,7 +223,7 @@ done # ------------------------------------------------------- echo "" echo "--- 4. A2A round-trip (first message) ---" -for runtime in claude-code hermes codex openclaw; do +for runtime in claude-code hermes codex openclaw google-adk; do id="${WS_IDS[$runtime]:-}" [ -z "$id" ] && continue reply=$(a2a_send "$id" "Reply with just the word OK so we know you got this.") @@ -213,7 +236,7 @@ done # ------------------------------------------------------- echo "" echo "--- 5. Session continuity (second message recalls first) ---" -for runtime in claude-code hermes codex openclaw; do +for runtime in claude-code hermes codex openclaw google-adk; do id="${WS_IDS[$runtime]:-}" [ -z "$id" ] && continue # Set up: tell the agent a name.