Live — external events (escalation → agent calls driver) #3
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Live — external events (escalation → agent calls driver) | |
| # Boots the agent-under-test (AUT) gateway, then POSTs a signed external | |
| # escalation webhook (a CI-escalation demo shape) at its local webhook listener | |
| # asking it to phone the driver contact. The test verifies the agent actually | |
| # places that call. The driver sits on auto_reject (set by the test) — we | |
| # monitor the escalation, not the call itself. | |
| # Real model + real call, so this runs only on ready (non-draft) PRs + dispatch, | |
| # and shares the AUT tunnel lock with the other live suites. | |
| on: | |
| pull_request: | |
| branches: [main, standardization] | |
| types: [opened, synchronize, reopened, ready_for_review] | |
| workflow_dispatch: | |
| inputs: | |
| timeout_s: | |
| description: "Seconds to wait for the agent to place the call" | |
| default: "200" | |
| permissions: | |
| contents: read | |
| concurrency: | |
| # Same group as the other live suites: only one holder of the AUT tunnel at a time. | |
| group: inkbox-live-aut-tunnel | |
| cancel-in-progress: false | |
| jobs: | |
| external-events: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| # Skip fork PRs (no secrets) and draft PRs (expensive). Dispatch always runs. | |
| if: >- | |
| (github.event_name != 'pull_request' || (github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.draft == false)) | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: "22" | |
| - name: Set up env paths | |
| run: | | |
| echo "CODEX_HOME=$RUNNER_TEMP/codex-home" >> "$GITHUB_ENV" | |
| echo "CODEX_PROJECT_DIR=$RUNNER_TEMP/project" >> "$GITHUB_ENV" | |
| echo "GATEWAY_LOG=$RUNNER_TEMP/gateway.log" >> "$GITHUB_ENV" | |
| mkdir -p "$RUNNER_TEMP/codex-home" "$RUNNER_TEMP/project" | |
| - name: Install bridge + test deps | |
| run: pip install -e . pytest | |
| # @alpha is the prerelease channel cut from codex main near-daily — the | |
| # freshest main build available without compiling the host from source. | |
| - name: Install Codex (freshest main prerelease) | |
| run: | | |
| npm install -g @openai/codex@alpha | |
| codex --version | |
| - name: Configure AUT identity + model | |
| env: | |
| CODEX_INKBOX_API_KEY: ${{ secrets.CODEX_INKBOX_API_KEY }} | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| run: | | |
| HANDLE="$(python3 - <<'PYEOF' | |
| import os | |
| from inkbox import Inkbox | |
| c = Inkbox(api_key=os.environ["CODEX_INKBOX_API_KEY"], base_url=os.environ.get("INKBOX_BASE_URL", "https://inkbox.ai")) | |
| print(c.mailboxes.list()[0].email_address.split("@", 1)[0]) | |
| PYEOF | |
| )" | |
| echo "AUT handle: $HANDLE" | |
| # Per-run GitHub webhook secret: shared by the gateway (to verify) and | |
| # the test (to sign). Generated fresh so nothing is committed. | |
| GH_SECRET="$(openssl rand -hex 24)" | |
| { | |
| echo "INKBOX_IDENTITY=$HANDLE" | |
| # NB: no INKBOX_ALLOW_ALL_USERS here on purpose — external events | |
| # are routed on their own external: sessions and must bypass user | |
| # auth on their own. Setting allow-all would mask a regression in | |
| # that bypass. | |
| # The whole point of this suite — let external webhooks reach the agent. | |
| echo "INKBOX_EXTERNAL_EVENTS_ENABLED=true" | |
| # Secret the github WebhookProvider verifies X-Hub-Signature-256 against. | |
| echo "INKBOX_WEBHOOK_SECRET_GITHUB=$GH_SECRET" | |
| # No realtime needed — the driver auto-rejects, so no media leg runs. | |
| echo "INKBOX_REALTIME_ENABLED=false" | |
| # Unattended runner: nobody is on the other end to answer an approval | |
| # text, so never escalate — and keep the sandbox read-only so a stray | |
| # command the model dreams up stays harmless. | |
| echo "CODEX_SANDBOX=read-only" | |
| echo "CODEX_APPROVAL_POLICY=never" | |
| # MCP tool confirmations are opt-in here: without this flag the | |
| # gateway escalates each Inkbox tool prompt as a poll nobody answers. | |
| echo "INKBOX_CODEX_AUTO_APPROVE_INKBOX_TOOLS=true" | |
| } >> "$GITHUB_ENV" | |
| # Real OpenAI via the default provider — authenticate the codex CLI | |
| # with the API key (writes auth.json under CODEX_HOME). | |
| printenv OPENAI_API_KEY | codex login --with-api-key | |
| echo "CODEX_MODEL=gpt-5.5" >> "$GITHUB_ENV" | |
| - name: Start gateway and wait for readiness | |
| env: | |
| INKBOX_API_KEY: ${{ secrets.CODEX_INKBOX_API_KEY }} | |
| INKBOX_SIGNING_KEY: ${{ secrets.CODEX_INKBOX_SIGNING_KEY }} | |
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | |
| run: | | |
| inkbox-codex run > "$GATEWAY_LOG" 2>&1 & | |
| echo $! > "$RUNNER_TEMP/gateway.pid" | |
| echo "Waiting for the gateway to be ready (tunnel + webhooks)…" | |
| for i in $(seq 1 36); do # up to ~180s | |
| if grep -q "tunnel ready" "$GATEWAY_LOG" && grep -q "\[bridge\] phone" "$GATEWAY_LOG"; then | |
| echo "Gateway ready."; exit 0 | |
| fi | |
| sleep 5 | |
| done | |
| echo "::error::gateway did not become ready"; cat "$GATEWAY_LOG"; exit 1 | |
| - name: Run external-event tests | |
| env: | |
| REMOTE_INKBOX_API_KEY: ${{ secrets.REMOTE_INKBOX_API_KEY }} | |
| CODEX_INKBOX_API_KEY: ${{ secrets.CODEX_INKBOX_API_KEY }} | |
| CODEX_INKBOX_SIGNING_KEY: ${{ secrets.CODEX_INKBOX_SIGNING_KEY }} | |
| LIVE_EXTERNAL_TIMEOUT: ${{ github.event.inputs.timeout_s || '200' }} | |
| run: | | |
| # Inkbox-signed escalation + GitHub-signed (valid & forged) escalation. | |
| LIVE_REAL_MODEL=1 AUT_WEBHOOK_URL=http://127.0.0.1:8767/webhook \ | |
| python3 -m pytest \ | |
| tests/live/test_external_event_intelligence.py \ | |
| tests/live/test_external_event_github.py -v | |
| # Failure-only: these logs carry live agent content and this repo | |
| # (and its Action logs/artifacts) is public. | |
| - name: Dump logs (on failure only) | |
| if: failure() | |
| run: | | |
| echo "=== gateway.log ==="; cat "$GATEWAY_LOG" || true | |
| - name: Tear down (always) | |
| if: always() | |
| run: | | |
| kill "$(cat "$RUNNER_TEMP/gateway.pid" 2>/dev/null)" 2>/dev/null || true | |
| sleep 3 | |
| - name: Upload artifacts (on failure only) | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: live-external-events-logs | |
| retention-days: 5 | |
| path: ${{ runner.temp }}/gateway.log | |
| if-no-files-found: ignore |