diff --git a/.github/workflows/daily-astrostylelite-markdown-spellcheck.lock.yml b/.github/workflows/daily-astrostylelite-markdown-spellcheck.lock.yml index 772aa9a7b14..f24ba666cd0 100644 --- a/.github/workflows/daily-astrostylelite-markdown-spellcheck.lock.yml +++ b/.github/workflows/daily-astrostylelite-markdown-spellcheck.lock.yml @@ -1410,6 +1410,9 @@ jobs: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Daily AstroStyleLite Markdown Spellcheck" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-astrostylelite-markdown-spellcheck.lock.yml@${{ github.ref }} - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: diff --git a/.github/workflows/daily-community-attribution.lock.yml b/.github/workflows/daily-community-attribution.lock.yml index b351a154881..59b584bc0f6 100644 --- a/.github/workflows/daily-community-attribution.lock.yml +++ b/.github/workflows/daily-community-attribution.lock.yml @@ -1472,6 +1472,9 @@ jobs: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Daily Community Attribution Updater" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/daily-community-attribution.lock.yml@${{ github.ref }} - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index 60e2c19224e..5ead38c3d83 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -720,7 +720,7 @@ jobs: printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.35/awf-config.schema.json","network":{"allowDomains":["api.githubcopilot.com","api.pi.ai","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.com","crl.geotrust.com","crl.globalsign.com","crl.identrust.com","crl.sectigo.com","crl.thawte.com","crl.usertrust.com","crl.verisign.com","crl3.digicert.com","crl4.digicert.com","crls.ssl.com","github.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.com","ocsp.digicert.com","ocsp.geotrust.com","ocsp.globalsign.com","ocsp.identrust.com","ocsp.sectigo.com","ocsp.ssl.com","ocsp.thawte.com","ocsp.usertrust.com","ocsp.verisign.com","packagecloud.io","packages.cloud.google.com","packages.microsoft.com","ppa.launchpad.net","raw.githubusercontent.com","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com"]},"apiProxy":{"enabled":true},"container":{"imageTag":"0.25.35"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json # shellcheck disable=SC1003 sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GH_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull --difc-proxy-host host.docker.internal:18443 --difc-proxy-ca-cert /tmp/gh-aw/difc-proxy-tls/ca.crt \ - -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && mkdir -p /tmp/gh-aw/pi-agent-dir && echo eyJwcm92aWRlcnMiOnsiYXctZ2F0ZXdheSI6eyJhcGkiOiJvcGVuYWktY29tcGxldGlvbnMiLCJhcGlLZXkiOiJDT1BJTE9UX0dJVEhVQl9UT0tFTiIsImJhc2VVcmwiOiJodHRwOi8vaG9zdC5kb2NrZXIuaW50ZXJuYWw6MTAwMDIiLCJtb2RlbHMiOlt7ImlkIjoiY2xhdWRlLXNvbm5ldC00LTIwMjUwNTE0In1dfX19 | base64 -d > /tmp/gh-aw/pi-agent-dir/models.json && cat /tmp/gh-aw/aw-prompts/prompt.txt | pi --print --mode json --no-session --model aw-gateway/claude-sonnet-4-20250514 --extension "${RUNNER_TEMP}/gh-aw/actions/pi_provider.cjs" --extension "${RUNNER_TEMP}/gh-aw/actions/pi_steering_extension.cjs" 2>&1 | tee /tmp/gh-aw/pi-streaming.jsonl' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && mkdir -p /tmp/gh-aw/pi-agent-dir && printf '\''{"providers":{"aw-gateway":{"api":"openai-completions","apiKey":"COPILOT_GITHUB_TOKEN","baseUrl":"%s","models":[{"id":"claude-sonnet-4-20250514"}]}}}'\'' "${GITHUB_COPILOT_BASE_URL}" > /tmp/gh-aw/pi-agent-dir/models.json && cat /tmp/gh-aw/aw-prompts/prompt.txt | pi --print --mode json --no-session --model aw-gateway/claude-sonnet-4-20250514 --extension "${RUNNER_TEMP}/gh-aw/actions/pi_provider.cjs" --extension "${RUNNER_TEMP}/gh-aw/actions/pi_steering_extension.cjs" 2>&1 | tee /tmp/gh-aw/pi-streaming.jsonl' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} GH_AW_PHASE: agent @@ -729,6 +729,7 @@ jobs: GH_AW_VERSION: dev GH_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || github.token }} GITHUB_AW: true + GITHUB_COPILOT_BASE_URL: http://host.docker.internal:10002 GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md GITHUB_WORKSPACE: ${{ github.workspace }} GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com @@ -1181,13 +1182,14 @@ jobs: printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.35/awf-config.schema.json","network":{"allowDomains":["api.githubcopilot.com","api.pi.ai","github.com","host.docker.internal","raw.githubusercontent.com","registry.npmjs.org"]},"apiProxy":{"enabled":true},"container":{"imageTag":"0.25.35"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json # shellcheck disable=SC1003 sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ - -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && mkdir -p /tmp/gh-aw/pi-agent-dir && echo eyJwcm92aWRlcnMiOnsiYXctZ2F0ZXdheSI6eyJhcGkiOiJvcGVuYWktY29tcGxldGlvbnMiLCJhcGlLZXkiOiJDT1BJTE9UX0dJVEhVQl9UT0tFTiIsImJhc2VVcmwiOiJodHRwOi8vaG9zdC5kb2NrZXIuaW50ZXJuYWw6MTAwMDIiLCJtb2RlbHMiOlt7ImlkIjoiY2xhdWRlLXNvbm5ldC00LTIwMjUwNTE0In1dfX19 | base64 -d > /tmp/gh-aw/pi-agent-dir/models.json && cat /tmp/gh-aw/aw-prompts/prompt.txt | pi --print --mode json --no-session --model aw-gateway/claude-sonnet-4-20250514 --extension "${RUNNER_TEMP}/gh-aw/actions/pi_provider.cjs" --extension "${RUNNER_TEMP}/gh-aw/actions/pi_steering_extension.cjs" 2>&1 | tee /tmp/gh-aw/pi-streaming.jsonl' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log + -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && mkdir -p /tmp/gh-aw/pi-agent-dir && printf '\''{"providers":{"aw-gateway":{"api":"openai-completions","apiKey":"COPILOT_GITHUB_TOKEN","baseUrl":"%s","models":[{"id":"claude-sonnet-4-20250514"}]}}}'\'' "${GITHUB_COPILOT_BASE_URL}" > /tmp/gh-aw/pi-agent-dir/models.json && cat /tmp/gh-aw/aw-prompts/prompt.txt | pi --print --mode json --no-session --model aw-gateway/claude-sonnet-4-20250514 --extension "${RUNNER_TEMP}/gh-aw/actions/pi_provider.cjs" --extension "${RUNNER_TEMP}/gh-aw/actions/pi_steering_extension.cjs" 2>&1 | tee /tmp/gh-aw/pi-streaming.jsonl' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log env: COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_VERSION: dev GITHUB_AW: true + GITHUB_COPILOT_BASE_URL: http://host.docker.internal:10002 GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md GITHUB_WORKSPACE: ${{ github.workspace }} GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 2f87486f47d..00ab0351a66 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -2441,6 +2441,9 @@ jobs: destination: ${{ runner.temp }}/gh-aw/actions job-name: ${{ github.job }} trace-id: ${{ needs.activation.outputs.setup-trace-id }} + env: + GH_AW_SETUP_WORKFLOW_NAME: "Smoke Copilot" + GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smoke-copilot.lock.yml@${{ github.ref }} - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: diff --git a/.github/workflows/smoke-pi.lock.yml b/.github/workflows/smoke-pi.lock.yml index 7a85089f74b..d8d123a8e09 100644 --- a/.github/workflows/smoke-pi.lock.yml +++ b/.github/workflows/smoke-pi.lock.yml @@ -912,7 +912,7 @@ jobs: printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.35/awf-config.schema.json","network":{"allowDomains":["*.githubusercontent.com","api.githubcopilot.com","api.pi.ai","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.com","codeload.github.com","crl.geotrust.com","crl.globalsign.com","crl.identrust.com","crl.sectigo.com","crl.thawte.com","crl.usertrust.com","crl.verisign.com","crl3.digicert.com","crl4.digicert.com","crls.ssl.com","docs.github.com","github-cloud.githubusercontent.com","github-cloud.s3.amazonaws.com","github.blog","github.com","github.githubassets.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.com","lfs.github.com","objects.githubusercontent.com","ocsp.digicert.com","ocsp.geotrust.com","ocsp.globalsign.com","ocsp.identrust.com","ocsp.sectigo.com","ocsp.ssl.com","ocsp.thawte.com","ocsp.usertrust.com","ocsp.verisign.com","packagecloud.io","packages.cloud.google.com","packages.microsoft.com","ppa.launchpad.net","raw.githubusercontent.com","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com"]},"apiProxy":{"enabled":true},"container":{"imageTag":"0.25.35"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json # shellcheck disable=SC1003 sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GH_AW_GH_TOKEN --exclude-env GH_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull --difc-proxy-host host.docker.internal:18443 --difc-proxy-ca-cert /tmp/gh-aw/difc-proxy-tls/ca.crt \ - -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && mkdir -p /tmp/gh-aw/pi-agent-dir && echo eyJwcm92aWRlcnMiOnsiYXctZ2F0ZXdheSI6eyJhcGkiOiJvcGVuYWktY29tcGxldGlvbnMiLCJhcGlLZXkiOiJDT1BJTE9UX0dJVEhVQl9UT0tFTiIsImJhc2VVcmwiOiJodHRwOi8vaG9zdC5kb2NrZXIuaW50ZXJuYWw6MTAwMDIiLCJtb2RlbHMiOlt7ImlkIjoiY2xhdWRlLXNvbm5ldC00LTIwMjUwNTE0In1dfX19 | base64 -d > /tmp/gh-aw/pi-agent-dir/models.json && cat /tmp/gh-aw/aw-prompts/prompt.txt | pi --print --mode json --no-session --model aw-gateway/claude-sonnet-4-20250514 --extension "${RUNNER_TEMP}/gh-aw/actions/pi_provider.cjs" --extension "${RUNNER_TEMP}/gh-aw/actions/pi_steering_extension.cjs" 2>&1 | tee /tmp/gh-aw/pi-streaming.jsonl' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && mkdir -p /tmp/gh-aw/pi-agent-dir && printf '\''{"providers":{"aw-gateway":{"api":"openai-completions","apiKey":"COPILOT_GITHUB_TOKEN","baseUrl":"%s","models":[{"id":"claude-sonnet-4-20250514"}]}}}'\'' "${GITHUB_COPILOT_BASE_URL}" > /tmp/gh-aw/pi-agent-dir/models.json && cat /tmp/gh-aw/aw-prompts/prompt.txt | pi --print --mode json --no-session --model aw-gateway/claude-sonnet-4-20250514 --extension "${RUNNER_TEMP}/gh-aw/actions/pi_provider.cjs" --extension "${RUNNER_TEMP}/gh-aw/actions/pi_steering_extension.cjs" 2>&1 | tee /tmp/gh-aw/pi-streaming.jsonl' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} GH_AW_PHASE: agent @@ -921,6 +921,7 @@ jobs: GH_AW_VERSION: dev GH_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || github.token }} GITHUB_AW: true + GITHUB_COPILOT_BASE_URL: http://host.docker.internal:10002 GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md GITHUB_WORKSPACE: ${{ github.workspace }} GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com @@ -1410,13 +1411,14 @@ jobs: printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.35/awf-config.schema.json","network":{"allowDomains":["api.githubcopilot.com","api.pi.ai","github.com","host.docker.internal","raw.githubusercontent.com","registry.npmjs.org"]},"apiProxy":{"enabled":true},"container":{"imageTag":"0.25.35"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json # shellcheck disable=SC1003 sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ - -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && mkdir -p /tmp/gh-aw/pi-agent-dir && echo eyJwcm92aWRlcnMiOnsiYXctZ2F0ZXdheSI6eyJhcGkiOiJvcGVuYWktY29tcGxldGlvbnMiLCJhcGlLZXkiOiJDT1BJTE9UX0dJVEhVQl9UT0tFTiIsImJhc2VVcmwiOiJodHRwOi8vaG9zdC5kb2NrZXIuaW50ZXJuYWw6MTAwMDIiLCJtb2RlbHMiOlt7ImlkIjoiY2xhdWRlLXNvbm5ldC00LTIwMjUwNTE0In1dfX19 | base64 -d > /tmp/gh-aw/pi-agent-dir/models.json && cat /tmp/gh-aw/aw-prompts/prompt.txt | pi --print --mode json --no-session --model aw-gateway/claude-sonnet-4-20250514 --extension "${RUNNER_TEMP}/gh-aw/actions/pi_provider.cjs" --extension "${RUNNER_TEMP}/gh-aw/actions/pi_steering_extension.cjs" 2>&1 | tee /tmp/gh-aw/pi-streaming.jsonl' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log + -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && mkdir -p /tmp/gh-aw/pi-agent-dir && printf '\''{"providers":{"aw-gateway":{"api":"openai-completions","apiKey":"COPILOT_GITHUB_TOKEN","baseUrl":"%s","models":[{"id":"claude-sonnet-4-20250514"}]}}}'\'' "${GITHUB_COPILOT_BASE_URL}" > /tmp/gh-aw/pi-agent-dir/models.json && cat /tmp/gh-aw/aw-prompts/prompt.txt | pi --print --mode json --no-session --model aw-gateway/claude-sonnet-4-20250514 --extension "${RUNNER_TEMP}/gh-aw/actions/pi_provider.cjs" --extension "${RUNNER_TEMP}/gh-aw/actions/pi_steering_extension.cjs" 2>&1 | tee /tmp/gh-aw/pi-streaming.jsonl' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log env: COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} GH_AW_PHASE: detection GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_VERSION: dev GITHUB_AW: true + GITHUB_COPILOT_BASE_URL: http://host.docker.internal:10002 GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md GITHUB_WORKSPACE: ${{ github.workspace }} GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com diff --git a/actions/setup/js/awf_reflect.cjs b/actions/setup/js/awf_reflect.cjs index dad907b95b7..a823d97e964 100644 --- a/actions/setup/js/awf_reflect.cjs +++ b/actions/setup/js/awf_reflect.cjs @@ -20,8 +20,10 @@ const path = require("path"); // AWF API proxy management endpoint for discovering configured LLM providers and available models. // The api-proxy sidecar exposes /reflect on its management port (port 10000) inside the AWF -// Docker network. From the agent container, the proxy is reachable via the "api-proxy" hostname. -const AWF_API_PROXY_REFLECT_URL = "http://api-proxy:10000/reflect"; +// Docker network. The sidecar's fixed container IP (172.30.0.30) is used instead of the +// "api-proxy" DNS hostname to ensure reachability from agent containers (such as Pi) that +// may run in a network context where the Docker service name does not resolve. +const AWF_API_PROXY_REFLECT_URL = "http://172.30.0.30:10000/reflect"; // Path inside the agent container where the reflect payload is persisted. The directory is // co-located with other AWF firewall observability data so it is included in the agent artifact. const AWF_REFLECT_OUTPUT_PATH = "/tmp/gh-aw/sandbox/firewall/awf-reflect.json"; @@ -143,8 +145,8 @@ async function enrichReflectModels(reflectData, timeoutMs, logger) { * Fetch the AWF API proxy /reflect endpoint and persist the response to disk. * * The /reflect endpoint is exposed by the api-proxy sidecar on its management port (10000) - * and returns the list of configured LLM providers together with their available model lists. - * This information is saved to AWF_REFLECT_OUTPUT_PATH so the post-run GitHub Actions step + * at the sidecar's fixed container IP (172.30.0.30) and returns the list of configured LLM + * providers together with their available model lists. This information is saved to AWF_REFLECT_OUTPUT_PATH so the post-run GitHub Actions step * (awf_reflect_summary.cjs) can include it in the step summary without requiring the * containers to still be running. * diff --git a/actions/setup/js/awf_reflect.test.cjs b/actions/setup/js/awf_reflect.test.cjs index a3b5a555537..9b5cc206e1b 100644 --- a/actions/setup/js/awf_reflect.test.cjs +++ b/actions/setup/js/awf_reflect.test.cjs @@ -20,7 +20,7 @@ const { describe("awf_reflect.cjs", () => { describe("constants", () => { it("exports expected default values", () => { - expect(AWF_API_PROXY_REFLECT_URL).toBe("http://api-proxy:10000/reflect"); + expect(AWF_API_PROXY_REFLECT_URL).toBe("http://172.30.0.30:10000/reflect"); expect(AWF_REFLECT_OUTPUT_PATH).toBe("/tmp/gh-aw/sandbox/firewall/awf-reflect.json"); expect(AWF_REFLECT_TIMEOUT_MS).toBe(5000); expect(AWF_MODELS_URL_TIMEOUT_MS).toBe(3000); diff --git a/actions/setup/js/pi_provider.cjs b/actions/setup/js/pi_provider.cjs index d22b6196b65..d5516f11ec9 100644 --- a/actions/setup/js/pi_provider.cjs +++ b/actions/setup/js/pi_provider.cjs @@ -66,10 +66,16 @@ function resolveGatewayUrl(provider) { /** * Pi provider extension for gh-aw. * - * Subscribes to the `agent_start` Pi SDK event and calls the AWF /reflect - * endpoint to discover and log the open LLM inference paths before the agent - * begins its first turn. This is best-effort: any network or parse error is - * logged but does not abort the agent session. + * Subscribes to the `agent_start` Pi SDK event to log the active provider and + * gateway URL. Registers a `process.once('beforeExit')` handler to fetch the + * AWF /reflect endpoint after the agent finishes its last turn — matching the + * timing used by copilot_harness.cjs and claude_harness.cjs, which call + * fetchAWFReflect after the agent subprocess exits. Calling /reflect at + * agent_start is too early: the api-proxy management endpoint may not yet be + * serving the full /reflect response before the first LLM turn completes. + * + * The reflect fetch is best-effort: any network or parse error is logged but + * does not abort the agent session or affect the process exit code. * * @param {any} pi - Pi ExtensionAPI instance * @returns {void} @@ -91,9 +97,18 @@ function piProviderExtension(pi) { } else { log(`model=${model || "(not set)"} (no provider prefix — defaulting to Copilot gateway)`); } + }); - // Fetch AWF API proxy reflection data and persist to disk so the post-run - // step summary (awf_reflect_summary.cjs) can include provider and model info. + // Fetch AWF API proxy reflection data after Pi finishes all turns. + // beforeExit fires when the Node.js event loop is draining (Pi is done), + // while the AWF container is still running — the same lifecycle point at + // which copilot_harness.cjs and claude_harness.cjs call fetchAWFReflect. + // Scheduling the async fetch here keeps the event loop alive until the + // request completes (or times out), then the process exits normally. + // Note: if Pi calls process.exit() directly, beforeExit will not fire and + // the reflect file will simply not be written (same as the previous + // agent_start failure path — the step summary will show no provider data). + process.once("beforeExit", async () => { await fetchAWFReflect({ reflectUrl: AWF_API_PROXY_REFLECT_URL, outputPath: AWF_REFLECT_OUTPUT_PATH, diff --git a/pkg/workflow/compiler_experiments.go b/pkg/workflow/compiler_experiments.go index 245b0d1da66..2c6dcdc7e07 100644 --- a/pkg/workflow/compiler_experiments.go +++ b/pkg/workflow/compiler_experiments.go @@ -596,7 +596,7 @@ func (c *Compiler) buildPushExperimentsStateJob(data *WorkflowData) (*Job, error if setupActionRef != "" || c.actionMode.IsScript() { steps = append(steps, c.generateCheckoutActionsFolder(data)...) traceID := fmt.Sprintf("${{ needs.%s.outputs.setup-trace-id }}", constants.ActivationJobName) - steps = append(steps, c.generateSetupStep(setupActionRef, SetupActionDestination, false, traceID)...) + steps = append(steps, c.generateSetupStep(data, setupActionRef, SetupActionDestination, false, traceID)...) } // Checkout step – configure git credentials without downloading workspace files. diff --git a/pkg/workflow/pi_engine.go b/pkg/workflow/pi_engine.go index ce6fe21a9f5..79463b49965 100644 --- a/pkg/workflow/pi_engine.go +++ b/pkg/workflow/pi_engine.go @@ -1,7 +1,6 @@ package workflow import ( - "encoding/base64" "encoding/json" "fmt" "maps" @@ -106,38 +105,55 @@ func piNativeProviderName(backend UniversalLLMBackend) string { } } -// buildPiModelsJSON returns a minimal Pi models.json payload that registers a -// single custom provider named "aw-gateway" pointing at the AWF LLM gateway -// sidecar. Pi's resolveConfigValue() resolves the "apiKey" value by looking -// up process.env[apiKey], so passing the secret env-var name (e.g. -// "COPILOT_GITHUB_TOKEN") causes Pi to automatically use the value that is -// already present in the container environment. +// buildPiModelsJSONSetup returns a shell command fragment that generates +// models.json at runtime by reading the base URL from the environment variable +// named by baseURLEnvVarName. Using runtime env-var substitution ensures the +// correct gateway URL is used inside the AWF container, which may configure the +// URL differently than the compile-time default. // -// All dynamic values are marshaled via encoding/json to prevent JSON injection. -func buildPiModelsJSON(gatewayPort int, secretEnvVarName, modelID string) string { - payload := map[string]any{ - "providers": map[string]any{ - "aw-gateway": map[string]any{ - "baseUrl": fmt.Sprintf("http://host.docker.internal:%d", gatewayPort), - "api": "openai-completions", - "apiKey": secretEnvVarName, - "models": []map[string]any{{"id": modelID}}, +// Pi's resolveConfigValue() resolves the "apiKey" value by looking up +// process.env[apiKey], so secretEnvVarName is passed as a literal string name +// (not the token value itself). baseUrl, however, must contain an actual URL, +// so it is filled at runtime via printf from the env var. +func buildPiModelsJSONSetup(baseURLEnvVarName, secretEnvVarName, modelID string) string { + // Use json.Marshal for the fixed parts (apiKey and modelID) so that any + // special characters are properly escaped, preventing JSON injection. + // baseUrl is substituted at runtime via printf; the %s placeholder remains + // as a printf format specifier. + type modelEntry struct { + ID string `json:"id"` + } + type providerConfig struct { + API string `json:"api"` + APIKey string `json:"apiKey"` + // baseUrl is a literal "%s" placeholder filled by printf at runtime. + BaseURLPlaceholder string `json:"baseUrl"` + Models []modelEntry `json:"models"` + } + type modelsPayload struct { + Providers map[string]providerConfig `json:"providers"` + } + + payload := modelsPayload{ + Providers: map[string]providerConfig{ + "aw-gateway": { + API: "openai-completions", + APIKey: secretEnvVarName, + BaseURLPlaceholder: "%s", + Models: []modelEntry{{ID: modelID}}, }, }, } + b, err := json.Marshal(payload) if err != nil { - // json.Marshal only fails for non-serialisable types; our map is always + // json.Marshal only fails for non-serialisable types; our struct is always // serialisable, so this branch is unreachable in practice. - panic(fmt.Sprintf("BUG: buildPiModelsJSON failed to marshal JSON: %v", err)) + panic(fmt.Sprintf("BUG: buildPiModelsJSONSetup failed to marshal JSON: %v", err)) } - return string(b) -} - -// encodeBase64 returns the standard base64 encoding of s. Used to safely -// embed arbitrary content in shell commands without shell-injection risks. -func encodeBase64(s string) string { - return base64.StdEncoding.EncodeToString([]byte(s)) + return fmt.Sprintf( + `mkdir -p /tmp/gh-aw/pi-agent-dir && printf '%s' "${%s}" > /tmp/gh-aw/pi-agent-dir/models.json && `, + string(b), baseURLEnvVarName) } // GetRequiredSecretNames returns the list of secrets required by the Pi engine. @@ -285,19 +301,17 @@ func (e *PiEngine) GetExecutionSteps(workflowData *WorkflowData, logFile string) modelID := extractPiModelID(workflowData.EngineConfig.Model) if firewallEnabled && len(profile.coreSecretNames) > 0 { // Firewall case: write a models.json that redirects Pi's LLM calls to the - // AWF gateway sidecar port. The "apiKey" field value is the name of the env - // var that holds the secret; Pi's resolveConfigValue() looks up + // AWF gateway sidecar. The "apiKey" field value is the name of the env var + // that holds the secret; Pi's resolveConfigValue() looks up // process.env[apiKey] to obtain the actual token value at runtime. // - // The JSON is base64-encoded before embedding in the shell command so that - // the content is injection-safe regardless of what characters it contains. - modelsJSON := buildPiModelsJSON(profile.gatewayPort, profile.coreSecretNames[0], modelID) - modelsJSONBase64 := encodeBase64(modelsJSON) - piModelsJSONSetup = fmt.Sprintf( - `mkdir -p /tmp/gh-aw/pi-agent-dir && echo %s | base64 -d > /tmp/gh-aw/pi-agent-dir/models.json && `, - modelsJSONBase64) + // The base URL is read from the base URL env var at runtime (e.g. + // GITHUB_COPILOT_BASE_URL) rather than being hardcoded at compile time. + // This ensures Pi uses the gateway URL that the AWF container configures, + // which may differ from the compile-time default. + piModelsJSONSetup = buildPiModelsJSONSetup(profile.baseURLEnvName, profile.coreSecretNames[0], modelID) piArgs = append(piArgs, "--model", "aw-gateway/"+modelID) - piLog.Printf("Pi: using models.json gateway routing for model %q via aw-gateway (port %d)", modelID, profile.gatewayPort) + piLog.Printf("Pi: using models.json gateway routing for model %q via aw-gateway (%s)", modelID, profile.baseURLEnvName) } else { // No firewall: use Pi's built-in provider so it can reach the real LLM API. nativeProvider := piNativeProviderName(backend) @@ -373,10 +387,9 @@ touch %s } // Build the environment map. Provider-specific credentials are injected via - // the backend profile. The base URL env var (e.g. GITHUB_COPILOT_BASE_URL) is - // NOT set for Pi because Pi v0.72+ does not read provider-specific base URL env - // vars; routing is instead handled through models.json (firewall case) or by Pi's - // native provider (no-firewall case). + // the backend profile. When routing through the AWF gateway, the base URL env + // var (e.g. GITHUB_COPILOT_BASE_URL) is also set so that the models.json + // generation command can read it at runtime inside the AWF container. env := map[string]string{ "GH_AW_PROMPT": "/tmp/gh-aw/aw-prompts/prompt.txt", "GITHUB_AW": "true", @@ -396,10 +409,12 @@ touch %s delete(env, "OPENAI_API_KEY") } - // When the models.json gateway approach is used, tell Pi where to find it. + // When the models.json gateway approach is used, tell Pi where to find it and + // inject the base URL env var so the models.json generation command can read it. if piModelsJSONSetup != "" { env["PI_CODING_AGENT_DIR"] = "/tmp/gh-aw/pi-agent-dir" - piLog.Printf("Pi: setting PI_CODING_AGENT_DIR for models.json gateway config") + env[profile.baseURLEnvName] = fmt.Sprintf("http://host.docker.internal:%d", profile.gatewayPort) + piLog.Printf("Pi: setting PI_CODING_AGENT_DIR and %s for models.json gateway config", profile.baseURLEnvName) } if workflowData.IsDetectionRun { diff --git a/pkg/workflow/pi_engine_test.go b/pkg/workflow/pi_engine_test.go index 61407f486a5..c5a0ab6915b 100644 --- a/pkg/workflow/pi_engine_test.go +++ b/pkg/workflow/pi_engine_test.go @@ -235,6 +235,74 @@ func TestPiEngine_GetExecutionSteps_ProviderPrefixAnthropic(t *testing.T) { assert.NotContains(t, stepText, "COPILOT_GITHUB_TOKEN", "anthropic/ prefix should not inject COPILOT_GITHUB_TOKEN") } +func TestBuildPiModelsJSONSetup_Copilot(t *testing.T) { + cmd := buildPiModelsJSONSetup("GITHUB_COPILOT_BASE_URL", "COPILOT_GITHUB_TOKEN", "claude-sonnet-4") + assert.Contains(t, cmd, `printf '`, "Should use printf for env-var substitution") + assert.Contains(t, cmd, "COPILOT_GITHUB_TOKEN", "Should include the secret env var name as apiKey") + assert.Contains(t, cmd, "claude-sonnet-4", "Should include the model ID") + assert.Contains(t, cmd, `${GITHUB_COPILOT_BASE_URL}`, "Should read base URL from env var at runtime") + assert.Contains(t, cmd, "aw-gateway", "Should register the aw-gateway provider") + assert.Contains(t, cmd, "openai-completions", "Should specify openai-completions API") + assert.NotContains(t, cmd, "host.docker.internal", "Should not hardcode host.docker.internal URL") + assert.NotContains(t, cmd, "base64", "Should not use base64 encoding") + assert.True(t, strings.HasSuffix(cmd, " && "), "Fragment should end with ' && ' for chaining") +} + +func TestBuildPiModelsJSONSetup_Anthropic(t *testing.T) { + cmd := buildPiModelsJSONSetup("ANTHROPIC_BASE_URL", "ANTHROPIC_API_KEY", "claude-opus-4") + assert.Contains(t, cmd, "ANTHROPIC_API_KEY", "Should include the secret env var name as apiKey") + assert.Contains(t, cmd, "claude-opus-4", "Should include the model ID") + assert.Contains(t, cmd, `${ANTHROPIC_BASE_URL}`, "Should read base URL from env var at runtime") +} + +func TestPiEngine_GetExecutionSteps_WithFirewall_UsesEnvVarBaseURL(t *testing.T) { + engine := NewPiEngine() + workflowData := makeFirewallEnabledWorkflowData() + workflowData.EngineConfig = &EngineConfig{ID: "pi", Model: "copilot/claude-sonnet-4"} + + steps := engine.GetExecutionSteps(workflowData, "/tmp/gh-aw/agent-stdio.log") + require.Len(t, steps, 1, "Should produce exactly one execution step") + + stepText := strings.Join(steps[0], "\n") + // The models.json should be generated at runtime via printf from an env var, + // NOT via echo ... | base64 -d. + assert.Contains(t, stepText, `printf '`, "Should generate models.json using printf for runtime env-var substitution") + assert.Contains(t, stepText, `${GITHUB_COPILOT_BASE_URL}`, "Should read base URL from env var at runtime") + assert.NotContains(t, stepText, "base64 -d", "Should not decode a hardcoded base64 URL") + assert.Contains(t, stepText, "GITHUB_COPILOT_BASE_URL", "Should set GITHUB_COPILOT_BASE_URL in step env for container") + assert.Contains(t, stepText, "host.docker.internal", "Step env should set GITHUB_COPILOT_BASE_URL to host.docker.internal URL") + assert.Contains(t, stepText, "PI_CODING_AGENT_DIR", "Should set PI_CODING_AGENT_DIR so Pi CLI reads models.json") + assert.Contains(t, stepText, "aw-gateway/claude-sonnet-4", "Should pass --model aw-gateway/ to Pi CLI") +} + +func TestPiEngine_GetExecutionSteps_WithFirewall_AnthropicUsesEnvVarBaseURL(t *testing.T) { + engine := NewPiEngine() + workflowData := makeFirewallEnabledWorkflowData() + workflowData.EngineConfig = &EngineConfig{ID: "pi", Model: "anthropic/claude-opus-4"} + + steps := engine.GetExecutionSteps(workflowData, "/tmp/gh-aw/agent-stdio.log") + require.Len(t, steps, 1, "Should produce exactly one execution step") + + stepText := strings.Join(steps[0], "\n") + assert.Contains(t, stepText, `${ANTHROPIC_BASE_URL}`, "Anthropic backend should use ANTHROPIC_BASE_URL env var") + assert.Contains(t, stepText, "ANTHROPIC_API_KEY", "Anthropic backend should set ANTHROPIC_API_KEY") + assert.NotContains(t, stepText, "GITHUB_COPILOT_BASE_URL", "Anthropic backend should not set Copilot URL") +} + +// makeFirewallEnabledWorkflowData returns WorkflowData with a firewall configuration +// that causes isFirewallEnabled() to return true. +func makeFirewallEnabledWorkflowData() *WorkflowData { + return &WorkflowData{ + Name: "test-workflow", + ParsedTools: NewTools(map[string]any{}), + NetworkPermissions: &NetworkPermissions{ + Firewall: &FirewallConfig{ + Enabled: true, + }, + }, + } +} + func TestPiEngine_ImplementsCodingAgentEngine(t *testing.T) { var _ CodingAgentEngine = NewPiEngine() }