Triage Panel #731
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
| # gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"81b598766ed96e8792d98abc5c3b63de6afc7129c129fba622aebd3159b05b6b","body_hash":"830c2609b0ebcec7a310810d8550606cb129d9d8fff2aa1bdcde0a0aa0a1f6a6","compiler_version":"v0.80.9","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.63"}} | |
| # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GH_AW_PLUGINS_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/checkout","sha":"9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0","version":"v7.0.0"},{"repo":"actions/create-github-app-token","sha":"1b10c78c7865c340bc4f6099eb2f838309f1e8c3","version":"v3.1.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7"},{"repo":"github/gh-aw-actions/setup","sha":"8c7d04ebf1ece56cd381446125da3e0f6896294a","version":"v0.80.9"},{"repo":"microsoft/apm-action","sha":"d723bb64ed70c135bbaf87d126b721dd2dae0439","version":"v1.10.0"},{"repo":"ruby/setup-ruby","sha":"89f90524b88a01fe6e0b732220432cc6142926af","version":"v1.313.0"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7","digest":"sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7","digest":"sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7","digest":"sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.27","digest":"sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b","pinned_image":"ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b"},{"image":"ghcr.io/github/github-mcp-server:v1.4.0","digest":"sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036","pinned_image":"ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036"}]} | |
| # This file was automatically generated by gh-aw (v0.80.9). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md | |
| # | |
| # ___ _ _ | |
| # / _ \ | | (_) | |
| # | |_| | __ _ ___ _ __ | |_ _ ___ | |
| # | _ |/ _` |/ _ \ '_ \| __| |/ __| | |
| # | | | | (_| | __/ | | | |_| | (__ | |
| # \_| |_/\__, |\___|_| |_|\__|_|\___| | |
| # __/ | | |
| # _ _ |___/ | |
| # | | | | / _| | | |
| # | | | | ___ _ __ _ __| |_| | _____ ____ | |
| # | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| | |
| # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ | |
| # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ | |
| # | |
| # | |
| # To update this file, edit the corresponding .md file and run: | |
| # gh aw compile | |
| # Not all edits will cause changes to this file. | |
| # | |
| # For more information: https://github.github.com/gh-aw/introduction/overview/ | |
| # | |
| # Auto-invoke the apm-triage-panel skill on a daily sweep of untriaged issues plus an opt-in fast path for explicit re-triage. Posts one synthesized verdict per issue and applies the panel-decided labels and milestone, with explicit "agentic proposal pending human ratification" framing. | |
| # | |
| # Resolved workflow manifest: | |
| # Imports: | |
| # - shared/apm.md | |
| # | |
| # Secrets used: | |
| # - COPILOT_GITHUB_TOKEN | |
| # - GH_AW_GITHUB_MCP_SERVER_TOKEN | |
| # - GH_AW_GITHUB_TOKEN | |
| # - GH_AW_PLUGINS_TOKEN | |
| # - GITHUB_TOKEN | |
| # | |
| # Custom actions used: | |
| # - actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 | |
| # - actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 | |
| # - actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| # - actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 | |
| # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| # - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | |
| # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 (source v7) | |
| # - github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9 | |
| # - microsoft/apm-action@d723bb64ed70c135bbaf87d126b721dd2dae0439 # v1.10.0 | |
| # - ruby/setup-ruby@89f90524b88a01fe6e0b732220432cc6142926af # v1.313.0 | |
| # | |
| # Container images used: | |
| # - ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c | |
| # - ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6 | |
| # - ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96 | |
| # - ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7 | |
| # - ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b | |
| # - ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036 | |
| name: "Triage Panel" | |
| on: | |
| issues: | |
| types: | |
| - labeled | |
| # roles: # Roles processed as role check in pre-activation job | |
| # - admin # Roles processed as role check in pre-activation job | |
| # - maintainer # Roles processed as role check in pre-activation job | |
| # - write # Roles processed as role check in pre-activation job | |
| schedule: | |
| - cron: "5 12 * * *" | |
| # Friendly format: daily (scattered) | |
| workflow_dispatch: | |
| inputs: | |
| aw_context: | |
| default: "" | |
| description: "Agent caller context (used internally by Agentic Workflows)." | |
| required: false | |
| type: string | |
| issue_number: | |
| description: "Optional: specific issue number to triage (overrides sweep). Leave blank to run the daily sweep on demand." | |
| required: false | |
| type: string | |
| permissions: {} | |
| concurrency: | |
| cancel-in-progress: false | |
| group: triage-panel-${{ github.event.issue.number || inputs.issue_number || 'sweep' }} | |
| run-name: "Triage Panel" | |
| jobs: | |
| activation: | |
| needs: pre_activation | |
| if: > | |
| needs.pre_activation.outputs.activated == 'true' && (github.event_name != 'issues' | |
| || (github.event.label.name == 'status/needs-triage' | |
| && github.event.issue.user.type != 'Bot' | |
| && github.event.issue.locked != true | |
| && github.event.issue.state == 'open')) | |
| runs-on: ubuntu-slim | |
| permissions: | |
| actions: read | |
| contents: read | |
| env: | |
| GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }} | |
| outputs: | |
| body: ${{ steps.sanitized.outputs.body }} | |
| comment_id: "" | |
| comment_repo: "" | |
| daily_ai_credits_exceeded: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_exceeded == 'true' }} | |
| daily_ai_credits_threshold: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_threshold || '' }} | |
| daily_ai_credits_total_effective_tokens: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_total_effective_tokens || '' }} | |
| engine_id: ${{ steps.generate_aw_info.outputs.engine_id }} | |
| lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }} | |
| model: ${{ steps.generate_aw_info.outputs.model }} | |
| secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} | |
| setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} | |
| setup-span-id: ${{ steps.setup.outputs.span-id }} | |
| setup-trace-id: ${{ steps.setup.outputs.trace-id }} | |
| stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }} | |
| text: ${{ steps.sanitized.outputs.text }} | |
| title: ${{ steps.sanitized.outputs.title }} | |
| steps: | |
| - name: Setup Scripts | |
| id: setup | |
| uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9 | |
| with: | |
| destination: ${{ runner.temp }}/gh-aw/actions | |
| job-name: ${{ github.job }} | |
| trace-id: ${{ needs.pre_activation.outputs.setup-trace-id }} | |
| parent-span-id: ${{ needs.pre_activation.outputs.setup-parent-span-id || needs.pre_activation.outputs.setup-span-id }} | |
| safe-output-artifact-client: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} | |
| env: | |
| GH_AW_SETUP_WORKFLOW_NAME: "Triage Panel" | |
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/triage-panel.lock.yml@${{ github.ref }} | |
| GH_AW_INFO_VERSION: "1.0.63" | |
| GH_AW_INFO_AWF_VERSION: "v0.27.7" | |
| GH_AW_INFO_ENGINE_ID: "copilot" | |
| - name: Generate agentic run info | |
| id: generate_aw_info | |
| env: | |
| GH_AW_INFO_ENGINE_ID: "copilot" | |
| GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" | |
| GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }} | |
| GH_AW_INFO_VERSION: "1.0.63" | |
| GH_AW_INFO_AGENT_VERSION: "1.0.63" | |
| GH_AW_INFO_CLI_VERSION: "v0.80.9" | |
| GH_AW_INFO_WORKFLOW_NAME: "Triage Panel" | |
| GH_AW_INFO_EXPERIMENTAL: "false" | |
| GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" | |
| GH_AW_INFO_STAGED: "false" | |
| GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github"]' | |
| GH_AW_INFO_FIREWALL_ENABLED: "true" | |
| GH_AW_INFO_AWF_VERSION: "v0.27.7" | |
| GH_AW_INFO_AWMG_VERSION: "" | |
| GH_AW_INFO_FIREWALL_TYPE: "squid" | |
| GH_AW_COMPILED_STRICT: "true" | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs'); | |
| await main(core, context); | |
| - name: Restore daily AIC usage cache | |
| id: restore-daily-aic-cache | |
| if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} | |
| continue-on-error: true | |
| uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 | |
| with: | |
| key: agentic-workflow-usage-triagepanel-${{ github.run_id }} | |
| restore-keys: agentic-workflow-usage-triagepanel- | |
| path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl | |
| - name: Restore daily AIC usage cache (artifact fallback) | |
| id: restore-daily-aic-cache-fallback | |
| if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} | |
| continue-on-error: true | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_RESTORE_DAILY_AIC_CACHE_HIT: ${{ steps.restore-daily-aic-cache.outputs.cache-hit }} | |
| GH_AW_RESTORE_DAILY_AIC_CACHE_MATCHED_KEY: ${{ steps.restore-daily-aic-cache.outputs.cache-matched-key }} | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/restore_aic_usage_cache_fallback.cjs'); | |
| await main(); | |
| - name: Check daily workflow token guardrail | |
| id: daily-effective-workflow-guardrail | |
| if: ${{ env.GH_AW_MAX_DAILY_AI_CREDITS != '' }} | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_WORKFLOW_NAME: "Triage Panel" | |
| GH_AW_WORKFLOW_ID: "triage-panel" | |
| GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| GH_AW_WORKFLOW_DISPATCH_AW_CONTEXT: ${{ github.event.inputs.aw_context || '' }} | |
| GH_AW_HAS_SLASH_COMMAND: "false" | |
| GH_AW_HAS_LABEL_COMMAND: "false" | |
| GH_AW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }} | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/check_daily_aic_workflow_guardrail.cjs'); | |
| await main(); | |
| - name: Validate COPILOT_GITHUB_TOKEN secret | |
| id: validate-secret | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_multi_secret.sh" COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default | |
| env: | |
| COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} | |
| - name: Checkout .github and .agents folders | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| persist-credentials: false | |
| sparse-checkout: | | |
| .github | |
| .agents | |
| .antigravity | |
| .claude | |
| .codex | |
| .crush | |
| .gemini | |
| .opencode | |
| .pi | |
| sparse-checkout-cone-mode: true | |
| fetch-depth: 1 | |
| - name: Save agent config folders for base branch restoration | |
| env: | |
| GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi" | |
| GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc" | |
| # poutine:ignore untrusted_checkout_exec | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh" | |
| - name: Check workflow lock file | |
| id: check-lock-file | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_WORKFLOW_FILE: "triage-panel.lock.yml" | |
| GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}" | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs'); | |
| await main(); | |
| - name: Check compile-agentic version | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_COMPILED_VERSION: "v0.80.9" | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/check_version_updates.cjs'); | |
| await main(); | |
| - name: Compute current body text | |
| id: sanitized | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,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,patch-diff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/compute_text.cjs'); | |
| await main(); | |
| - name: Create prompt with built-in context | |
| env: | |
| GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt | |
| GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl | |
| GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | |
| GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | |
| GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | |
| GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} | |
| GH_AW_GITHUB_ACTOR: ${{ github.actor }} | |
| GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} | |
| GH_AW_GITHUB_EVENT_NAME: ${{ github.event_name }} | |
| GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} | |
| GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} | |
| GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} | |
| GH_AW_INPUTS_ISSUE_NUMBER: ${{ inputs.issue_number }} | |
| # poutine:ignore untrusted_checkout_exec | |
| run: | | |
| bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" | |
| { | |
| cat << 'GH_AW_PROMPT_40fef7272a38a6be_EOF' | |
| <system> | |
| GH_AW_PROMPT_40fef7272a38a6be_EOF | |
| cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" | |
| cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" | |
| cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" | |
| cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" | |
| cat << 'GH_AW_PROMPT_40fef7272a38a6be_EOF' | |
| <safe-output-tools> | |
| Tools: add_comment(max:12), add_labels(max:70), remove_labels(max:12), assign_milestone(max:12), dispatch_workflow(max:10), missing_tool, missing_data, noop | |
| </safe-output-tools> | |
| GH_AW_PROMPT_40fef7272a38a6be_EOF | |
| cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md" | |
| cat << 'GH_AW_PROMPT_40fef7272a38a6be_EOF' | |
| <github-context> | |
| The following GitHub context information is available for this workflow: | |
| {{#if github.actor}} | |
| - **actor**: __GH_AW_GITHUB_ACTOR__ | |
| {{/if}} | |
| {{#if github.repository}} | |
| - **repository**: __GH_AW_GITHUB_REPOSITORY__ | |
| {{/if}} | |
| {{#if github.workspace}} | |
| - **workspace**: __GH_AW_GITHUB_WORKSPACE__ | |
| {{/if}} | |
| {{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}} | |
| - **issue-number**: #__GH_AW_EXPR_802A9F6A__ | |
| {{/if}} | |
| {{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}} | |
| - **discussion-number**: #__GH_AW_EXPR_1A3A194A__ | |
| {{/if}} | |
| {{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}} | |
| - **pull-request-number**: #__GH_AW_EXPR_463A214A__ | |
| {{/if}} | |
| {{#if github.event.comment.id || github.aw.context.comment_id}} | |
| - **comment-id**: __GH_AW_EXPR_FF1D34CE__ | |
| {{/if}} | |
| {{#if github.run_id}} | |
| - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ | |
| {{/if}} | |
| </github-context> | |
| GH_AW_PROMPT_40fef7272a38a6be_EOF | |
| cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" | |
| cat << 'GH_AW_PROMPT_40fef7272a38a6be_EOF' | |
| </system> | |
| {{#runtime-import .github/workflows/triage-panel.md}} | |
| GH_AW_PROMPT_40fef7272a38a6be_EOF | |
| } > "$GH_AW_PROMPT" | |
| - name: Interpolate variables and render templates | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt | |
| GH_AW_ENGINE_ID: "copilot" | |
| GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} | |
| GH_AW_GITHUB_EVENT_NAME: ${{ github.event_name }} | |
| GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} | |
| GH_AW_INPUTS_ISSUE_NUMBER: ${{ inputs.issue_number }} | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/interpolate_prompt.cjs'); | |
| await main(); | |
| - name: Substitute placeholders | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt | |
| GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | |
| GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | |
| GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} | |
| GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }} | |
| GH_AW_GITHUB_ACTOR: ${{ github.actor }} | |
| GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} | |
| GH_AW_GITHUB_EVENT_NAME: ${{ github.event_name }} | |
| GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} | |
| GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} | |
| GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} | |
| GH_AW_INPUTS_ISSUE_NUMBER: ${{ inputs.issue_number }} | |
| GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools' | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }} | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs'); | |
| // Call the substitution function | |
| return await substitutePlaceholders({ | |
| file: process.env.GH_AW_PROMPT, | |
| substitutions: { | |
| GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A, | |
| GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A, | |
| GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A, | |
| GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE, | |
| GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, | |
| GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, | |
| GH_AW_GITHUB_EVENT_NAME: process.env.GH_AW_GITHUB_EVENT_NAME, | |
| GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, | |
| GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, | |
| GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, | |
| GH_AW_INPUTS_ISSUE_NUMBER: process.env.GH_AW_INPUTS_ISSUE_NUMBER, | |
| GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST, | |
| GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED | |
| } | |
| }); | |
| - name: Validate prompt placeholders | |
| env: | |
| GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt | |
| # poutine:ignore untrusted_checkout_exec | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh" | |
| - name: Print prompt | |
| env: | |
| GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt | |
| # poutine:ignore untrusted_checkout_exec | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh" | |
| - name: Upload activation artifact | |
| if: success() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: activation | |
| include-hidden-files: true | |
| path: | | |
| /tmp/gh-aw/aw_info.json | |
| /tmp/gh-aw/models.json | |
| /tmp/gh-aw/aw-prompts/prompt.txt | |
| /tmp/gh-aw/aw-prompts/prompt-template.txt | |
| /tmp/gh-aw/aw-prompts/prompt-import-tree.json | |
| /tmp/gh-aw/github_rate_limits.jsonl | |
| /tmp/gh-aw/base | |
| /tmp/gh-aw/.github/agents | |
| /tmp/gh-aw/.github/skills | |
| if-no-files-found: ignore | |
| retention-days: 1 | |
| agent: | |
| needs: | |
| - activation | |
| - apm | |
| - apm-prep | |
| if: needs.activation.outputs.daily_ai_credits_exceeded != 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| issues: read | |
| pull-requests: read | |
| env: | |
| DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} | |
| GH_AW_ASSETS_ALLOWED_EXTS: "" | |
| GH_AW_ASSETS_BRANCH: "" | |
| GH_AW_ASSETS_MAX_SIZE_KB: 0 | |
| GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs | |
| GH_AW_WORKFLOW_ID_SANITIZED: triagepanel | |
| outputs: | |
| agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }} | |
| ai_credits_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.ai_credits_rate_limit_error || 'false' }} | |
| aic: ${{ steps.parse-mcp-gateway.outputs.aic }} | |
| ambient_context: ${{ steps.parse-mcp-gateway.outputs.ambient_context }} | |
| checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} | |
| effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }} | |
| has_patch: ${{ steps.collect_output.outputs.has_patch }} | |
| inference_access_error: ${{ steps.detect-agent-errors.outputs.inference_access_error || 'false' }} | |
| mcp_policy_error: ${{ steps.detect-agent-errors.outputs.mcp_policy_error || 'false' }} | |
| model: ${{ needs.activation.outputs.model }} | |
| model_not_supported_error: ${{ steps.detect-agent-errors.outputs.model_not_supported_error || 'false' }} | |
| output: ${{ steps.collect_output.outputs.output }} | |
| output_types: ${{ steps.collect_output.outputs.output_types }} | |
| setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} | |
| setup-span-id: ${{ steps.setup.outputs.span-id }} | |
| setup-trace-id: ${{ steps.setup.outputs.trace-id }} | |
| unknown_model_ai_credits: ${{ steps.parse-mcp-gateway.outputs.unknown_model_ai_credits || 'false' }} | |
| steps: | |
| - name: Setup Scripts | |
| id: setup | |
| uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9 | |
| with: | |
| destination: ${{ runner.temp }}/gh-aw/actions | |
| job-name: ${{ github.job }} | |
| trace-id: ${{ needs.activation.outputs.setup-trace-id }} | |
| parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} | |
| env: | |
| GH_AW_SETUP_WORKFLOW_NAME: "Triage Panel" | |
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/triage-panel.lock.yml@${{ github.ref }} | |
| GH_AW_INFO_VERSION: "1.0.63" | |
| GH_AW_INFO_AWF_VERSION: "v0.27.7" | |
| GH_AW_INFO_ENGINE_ID: "copilot" | |
| - name: Set runtime paths | |
| id: set-runtime-paths | |
| run: | | |
| { | |
| echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl" | |
| echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" | |
| echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Checkout repository | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| persist-credentials: false | |
| - name: Setup Ruby | |
| uses: ruby/setup-ruby@89f90524b88a01fe6e0b732220432cc6142926af # v1.313.0 | |
| with: | |
| ruby-version: '3.3' | |
| - name: Create gh-aw temp directory | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh" | |
| - name: Configure gh CLI for GitHub Enterprise | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh" | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| - name: Download APM bundle artifacts (all groups) | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| merge-multiple: false | |
| path: /tmp/gh-aw/apm-bundles | |
| pattern: ${{ needs.activation.outputs.artifact_prefix }}apm-* | |
| - env: | |
| ARTIFACT_PREFIX: ${{ needs.activation.outputs.artifact_prefix }} | |
| EXPECTED_MATRIX: ${{ needs.apm-prep.outputs.matrix }} | |
| name: Normalise bundle layout (single-artifact flatten workaround) | |
| run: "set -euo pipefail\n# actions/download-artifact (>=v5) flattens contents directly into `path/`\n# whenever exactly one artifact matches the pattern, ignoring\n# `merge-multiple: false`. Re-shape into the per-group subdir layout so\n# downstream validation sees a stable structure regardless of matrix size.\n# Upstream reference:\n# https://github.com/actions/download-artifact/blob/v8.0.1/src/download-artifact.ts\n# (see the `isSingleArtifactDownload || mergeMultiple || artifacts.length === 1`\n# branch). Remove this step once download-artifact stops flattening or\n# exposes an opt-out.\nexpected_count=$(echo \"$EXPECTED_MATRIX\" | jq '.group // [] | length')\nif [ \"$expected_count\" -eq 1 ]; then\n group_id=$(echo \"$EXPECTED_MATRIX\" | jq -r '.group[0].id')\n # Defence-in-depth: group_id is interpolated into a shell path. apm-prep\n # produces a sanitised id today, but enforce a strict allowlist here so\n # any future schema drift cannot smuggle traversal sequences.\n if ! printf '%s' \"$group_id\" | grep -Eq '^[A-Za-z0-9_-]+$'; then\n echo \"::error::unsafe group_id '$group_id' (must match ^[A-Za-z0-9_-]+$)\"\n exit 1\n fi\n group_dir=\"/tmp/gh-aw/apm-bundles/${ARTIFACT_PREFIX}apm-${group_id}\"\n if [ ! -d \"$group_dir\" ]; then\n mkdir -p \"$group_dir\"\n find /tmp/gh-aw/apm-bundles -mindepth 1 -maxdepth 1 ! -path \"$group_dir\" -exec mv {} \"$group_dir/\" \\;\n fi\nfi\n" | |
| - env: | |
| ARTIFACT_PREFIX: ${{ needs.activation.outputs.artifact_prefix }} | |
| EXPECTED_MATRIX: ${{ needs.apm-prep.outputs.matrix }} | |
| name: Validate downloaded bundles match matrix manifest | |
| run: "set -euo pipefail\nexpected=$(echo \"$EXPECTED_MATRIX\" | jq -r --arg prefix \"$ARTIFACT_PREFIX\" '.group | map($prefix + \"apm-\" + .id) | sort | .[]')\nactual=$(ls /tmp/gh-aw/apm-bundles | sort)\nmissing=$(comm -23 <(echo \"$expected\") <(echo \"$actual\") || true)\nunexpected=$(comm -13 <(echo \"$expected\") <(echo \"$actual\") || true)\nif [ -n \"$missing\" ]; then\n echo \"::error::missing APM bundles (group did not pack successfully): $missing\"\n exit 1\nfi\nif [ -n \"$unexpected\" ]; then\n echo \"::error::unexpected artifact in apm bundle download (collision attack?): $unexpected\"\n exit 1\nfi\n" | |
| - id: bundles | |
| name: Build bundle list | |
| run: "set -euo pipefail\nmapfile -t list < <(find /tmp/gh-aw/apm-bundles \\( -name '*.tar.gz' -o -name '*.zip' \\) | sort)\n[ ${#list[@]} -gt 0 ] || { echo '::error::no apm bundles found'; exit 1; }\nprintf '%s\\n' \"${list[@]}\" > /tmp/gh-aw/apm-bundle-list.txt\n" | |
| - name: Restore APM packages (all bundles) | |
| uses: microsoft/apm-action@d723bb64ed70c135bbaf87d126b721dd2dae0439 # v1.10.0 | |
| with: | |
| apm-version: 0.21.0 | |
| bundles-file: /tmp/gh-aw/apm-bundle-list.txt | |
| - name: Configure Git credentials | |
| env: | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| GITHUB_SERVER_URL: ${{ github.server_url }} | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh" | |
| - name: Checkout PR branch | |
| id: checkout-pr | |
| if: | | |
| github.event.pull_request || github.event.issue.pull_request || github.event_name == 'workflow_dispatch' && fromJSON(github.event.inputs.aw_context || '{}').item_type == 'pull_request' | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs'); | |
| await main(); | |
| - name: Install GitHub Copilot CLI | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.63 | |
| env: | |
| GH_HOST: github.com | |
| - name: Install AWF binary | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.7 | |
| - name: Parse integrity filter lists | |
| id: parse-guard-vars | |
| env: | |
| GH_AW_BLOCKED_USERS_VAR: ${{ vars.GH_AW_GITHUB_BLOCKED_USERS || '' }} | |
| GH_AW_TRUSTED_USERS_VAR: ${{ vars.GH_AW_GITHUB_TRUSTED_USERS || '' }} | |
| GH_AW_APPROVAL_LABELS_VAR: ${{ vars.GH_AW_GITHUB_APPROVAL_LABELS || '' }} | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/parse_guard_list.sh" | |
| - name: Download activation artifact | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: activation | |
| path: /tmp/gh-aw | |
| - name: Restore agent config folders from base branch | |
| if: steps.checkout-pr.outcome == 'success' | |
| env: | |
| GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi" | |
| GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc" | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh" | |
| - name: Restore inline sub-agents from activation artifact | |
| env: | |
| GH_AW_SUB_AGENT_DIR: ".github/agents" | |
| GH_AW_SUB_AGENT_EXT: ".agent.md" | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh" | |
| - name: Restore inline skills from activation artifact | |
| env: | |
| GH_AW_SKILL_DIR: ".github/skills" | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh" | |
| - name: Download container images | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6 ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96 ghcr.io/github/gh-aw-mcpg:v0.3.27@sha256:fe984bddde4ec05d756d9043edb0a32912e6b7b72f6a121b1082f29221421cc7 ghcr.io/github/gh-aw-node@sha256:529d02eb970b1161aa25c593a9c3df57fdfad5a8add328cb3b6eccef66f3183b ghcr.io/github/github-mcp-server:v1.4.0@sha256:2afb26356481d1a350e14544a6e160f7f7ec1561a1ea309b823665abf0309036 | |
| - name: Generate Safe Outputs Config | |
| run: | | |
| mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" | |
| mkdir -p /tmp/gh-aw/safeoutputs | |
| mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs | |
| cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_bd931bad288b62a5_EOF' | |
| {"add_comment":{"max":12},"add_labels":{"allowed":["theme/governance","theme/portability","theme/security","area/audit-policy","area/ci-cd","area/cli","area/content-security","area/distribution","area/docs-site","area/enterprise","area/lockfile","area/marketplace","area/mcp-config","area/mcp-trust","area/multi-target","area/package-authoring","area/testing","type/architecture","type/automation","type/bug","type/docs","type/feature","type/performance","type/refactor","type/release","priority/high","priority/low","status/accepted","status/blocked","status/in-flight","status/needs-design","status/triaged","good first issue","help wanted","test/triage-validation"],"max":70,"target":"*"},"assign_milestone":{"max":12},"create_report_incomplete_issue":{},"dispatch_workflow":{"max":10,"workflow_files":{"project-sync":".yml"},"workflows":["project-sync"]},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"remove_labels":{"allowed":["status/needs-triage"],"max":12,"target":"*"},"report_incomplete":{}} | |
| GH_AW_SAFE_OUTPUTS_CONFIG_bd931bad288b62a5_EOF | |
| - name: Generate Safe Outputs Tools | |
| env: | |
| GH_AW_TOOLS_META_JSON: | | |
| { | |
| "description_suffixes": { | |
| "add_comment": " CONSTRAINTS: Maximum 12 comment(s) can be added. Supports reply_to_id for discussion threading.", | |
| "add_labels": " CONSTRAINTS: Maximum 70 label(s) can be added. Only these labels are allowed: [\"theme/governance\" \"theme/portability\" \"theme/security\" \"area/audit-policy\" \"area/ci-cd\" \"area/cli\" \"area/content-security\" \"area/distribution\" \"area/docs-site\" \"area/enterprise\" \"area/lockfile\" \"area/marketplace\" \"area/mcp-config\" \"area/mcp-trust\" \"area/multi-target\" \"area/package-authoring\" \"area/testing\" \"type/architecture\" \"type/automation\" \"type/bug\" \"type/docs\" \"type/feature\" \"type/performance\" \"type/refactor\" \"type/release\" \"priority/high\" \"priority/low\" \"status/accepted\" \"status/blocked\" \"status/in-flight\" \"status/needs-design\" \"status/triaged\" \"good first issue\" \"help wanted\" \"test/triage-validation\"]. Target: *.", | |
| "assign_milestone": " CONSTRAINTS: Maximum 12 milestone assignment(s) can be made.", | |
| "remove_labels": " CONSTRAINTS: Maximum 12 label(s) can be removed. Only these labels can be removed: [status/needs-triage]. Target: *." | |
| }, | |
| "repo_params": {}, | |
| "dynamic_tools": [ | |
| { | |
| "_workflow_name": "project-sync", | |
| "description": "Dispatch the 'project-sync' workflow with workflow_dispatch trigger. This workflow must support workflow_dispatch and be in .github/workflows/ directory in the same repository.", | |
| "inputSchema": { | |
| "additionalProperties": false, | |
| "properties": { | |
| "content_id": { | |
| "description": "Issue or PR GraphQL node ID (e.g. I_kwDO... / PR_kwDO...). Obtain via: gh api graphql -f query='query{repository(owner:\"microsoft\",name:\"apm\"){issue(number:N){id}}}'", | |
| "type": "string" | |
| } | |
| }, | |
| "required": [ | |
| "content_id" | |
| ], | |
| "type": "object" | |
| }, | |
| "name": "project_sync" | |
| } | |
| ] | |
| } | |
| GH_AW_VALIDATION_JSON: | | |
| { | |
| "add_comment": { | |
| "defaultMax": 1, | |
| "fields": { | |
| "body": { | |
| "required": true, | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 65000 | |
| }, | |
| "item_number": { | |
| "issueOrPRNumber": true | |
| }, | |
| "reply_to_id": { | |
| "type": "string", | |
| "maxLength": 256 | |
| }, | |
| "repo": { | |
| "type": "string", | |
| "maxLength": 256 | |
| } | |
| } | |
| }, | |
| "add_labels": { | |
| "defaultMax": 5, | |
| "fields": { | |
| "item_number": { | |
| "issueNumberOrTemporaryId": true | |
| }, | |
| "labels": { | |
| "required": true, | |
| "type": "array", | |
| "itemType": "string", | |
| "itemSanitize": true, | |
| "itemMaxLength": 128 | |
| }, | |
| "repo": { | |
| "type": "string", | |
| "maxLength": 256 | |
| } | |
| } | |
| }, | |
| "assign_milestone": { | |
| "defaultMax": 1, | |
| "fields": { | |
| "issue_number": { | |
| "issueNumberOrTemporaryId": true | |
| }, | |
| "milestone_number": { | |
| "optionalPositiveInteger": true | |
| }, | |
| "milestone_title": { | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 128 | |
| }, | |
| "repo": { | |
| "type": "string", | |
| "maxLength": 256 | |
| } | |
| }, | |
| "customValidation": "requiresOneOf:milestone_number,milestone_title" | |
| }, | |
| "dispatch_workflow": { | |
| "defaultMax": 1, | |
| "fields": { | |
| "inputs": { | |
| "type": "object" | |
| }, | |
| "workflow_name": { | |
| "required": true, | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 256, | |
| "minLength": 1, | |
| "pattern": ".*\\S.*", | |
| "patternError": "must not be empty" | |
| } | |
| } | |
| }, | |
| "missing_data": { | |
| "defaultMax": 20, | |
| "fields": { | |
| "alternatives": { | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 256 | |
| }, | |
| "context": { | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 256 | |
| }, | |
| "data_type": { | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 128 | |
| }, | |
| "reason": { | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 256 | |
| } | |
| } | |
| }, | |
| "missing_tool": { | |
| "defaultMax": 20, | |
| "fields": { | |
| "alternatives": { | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 512 | |
| }, | |
| "reason": { | |
| "required": true, | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 256 | |
| }, | |
| "tool": { | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 128 | |
| } | |
| } | |
| }, | |
| "noop": { | |
| "defaultMax": 1, | |
| "fields": { | |
| "message": { | |
| "required": true, | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 65000 | |
| } | |
| } | |
| }, | |
| "remove_labels": { | |
| "defaultMax": 5, | |
| "fields": { | |
| "item_number": { | |
| "issueNumberOrTemporaryId": true | |
| }, | |
| "labels": { | |
| "required": true, | |
| "type": "array", | |
| "itemType": "string", | |
| "itemSanitize": true, | |
| "itemMaxLength": 128 | |
| }, | |
| "repo": { | |
| "type": "string", | |
| "maxLength": 256 | |
| } | |
| } | |
| }, | |
| "report_incomplete": { | |
| "defaultMax": 5, | |
| "fields": { | |
| "details": { | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 65000 | |
| }, | |
| "reason": { | |
| "required": true, | |
| "type": "string", | |
| "sanitize": true, | |
| "maxLength": 1024 | |
| } | |
| } | |
| } | |
| } | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_safe_outputs_tools.cjs'); | |
| await main(); | |
| - name: Start MCP Gateway | |
| id: start-mcp-gateway | |
| env: | |
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | |
| GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_CONFIG_PATH }} | |
| GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS_TOOLS_PATH }} | |
| GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -eo pipefail | |
| mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config" | |
| # Export gateway environment variables for MCP config and gateway script | |
| export MCP_GATEWAY_PORT="8080" | |
| export MCP_GATEWAY_DOMAIN="host.docker.internal" | |
| export MCP_GATEWAY_HOST_DOMAIN="localhost" | |
| MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') | |
| echo "::add-mask::${MCP_GATEWAY_API_KEY}" | |
| export MCP_GATEWAY_API_KEY | |
| export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" | |
| mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" | |
| export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" | |
| export DEBUG="*" | |
| export GH_AW_ENGINE="copilot" | |
| MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0') | |
| MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0') | |
| case "${DOCKER_HOST:-}" in | |
| unix://* ) DOCKER_SOCK_PATH="${DOCKER_HOST#unix://}" ;; | |
| /* ) DOCKER_SOCK_PATH="$DOCKER_HOST" ;; | |
| * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;; | |
| esac | |
| DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0') | |
| export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --name awmg-mcpg --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e RUNNER_TEMP -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw -v '"${RUNNER_TEMP}"'/gh-aw/safeoutputs:'"${RUNNER_TEMP}"'/gh-aw/safeoutputs:rw ghcr.io/github/gh-aw-mcpg:v0.3.27' | |
| mkdir -p "$HOME/.copilot" | |
| GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) | |
| cat << GH_AW_MCP_CONFIG_215d4fef0c41dfe2_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" | |
| { | |
| "mcpServers": { | |
| "github": { | |
| "type": "stdio", | |
| "container": "ghcr.io/github/github-mcp-server:v1.4.0", | |
| "env": { | |
| "GITHUB_HOST": "\${GITHUB_SERVER_URL}", | |
| "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", | |
| "GITHUB_READ_ONLY": "1", | |
| "GITHUB_TOOLSETS": "context,repos,issues,pull_requests" | |
| }, | |
| "guard-policies": { | |
| "allow-only": { | |
| "approval-labels": ${{ steps.parse-guard-vars.outputs.approval_labels }}, | |
| "blocked-users": ${{ steps.parse-guard-vars.outputs.blocked_users }}, | |
| "min-integrity": "none", | |
| "repos": [ | |
| "microsoft/apm" | |
| ], | |
| "trusted-users": ${{ steps.parse-guard-vars.outputs.trusted_users }} | |
| } | |
| } | |
| }, | |
| "safeoutputs": { | |
| "type": "stdio", | |
| "container": "ghcr.io/github/gh-aw-node", | |
| "mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "${RUNNER_TEMP}/gh-aw/safeoutputs:${RUNNER_TEMP}/gh-aw/safeoutputs:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"], | |
| "args": ["-w", "\${GITHUB_WORKSPACE}"], | |
| "entrypoint": "sh", | |
| "entrypointArgs": ["-c", "sh ${RUNNER_TEMP}/gh-aw/safeoutputs/start_safe_outputs_mcp.sh"], | |
| "env": { | |
| "DEBUG": "*", | |
| "DEFAULT_BRANCH": "\${DEFAULT_BRANCH}", | |
| "GH_AW_ASSETS_ALLOWED_EXTS": "\${GH_AW_ASSETS_ALLOWED_EXTS}", | |
| "GH_AW_ASSETS_BRANCH": "\${GH_AW_ASSETS_BRANCH}", | |
| "GH_AW_ASSETS_MAX_SIZE_KB": "\${GH_AW_ASSETS_MAX_SIZE_KB}", | |
| "GH_AW_MCP_LOG_DIR": "\${GH_AW_MCP_LOG_DIR}", | |
| "GH_AW_SAFE_OUTPUTS": "\${GH_AW_SAFE_OUTPUTS}", | |
| "GH_AW_SAFE_OUTPUTS_CONFIG_PATH": "\${GH_AW_SAFE_OUTPUTS_CONFIG_PATH}", | |
| "GH_AW_SAFE_OUTPUTS_TOOLS_PATH": "\${GH_AW_SAFE_OUTPUTS_TOOLS_PATH}", | |
| "GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}", | |
| "GITHUB_TOKEN": "\${GITHUB_TOKEN}", | |
| "GITHUB_WORKSPACE": "\${GITHUB_WORKSPACE}", | |
| "RUNNER_TEMP": "\${RUNNER_TEMP}" | |
| }, | |
| "guard-policies": { | |
| "write-sink": { | |
| "accept": [ | |
| "private:microsoft/apm" | |
| ] | |
| } | |
| } | |
| } | |
| }, | |
| "gateway": { | |
| "port": $MCP_GATEWAY_PORT, | |
| "domain": "${MCP_GATEWAY_DOMAIN}", | |
| "apiKey": "${MCP_GATEWAY_API_KEY}", | |
| "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" | |
| } | |
| } | |
| GH_AW_MCP_CONFIG_215d4fef0c41dfe2_EOF | |
| - name: Mount MCP servers as CLIs | |
| id: mount-mcp-clis | |
| continue-on-error: true | |
| env: | |
| MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} | |
| MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }} | |
| MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs'); | |
| await main(); | |
| - name: Clean credentials | |
| continue-on-error: true | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh" | |
| - name: Audit pre-agent workspace | |
| id: pre_agent_audit | |
| continue-on-error: true | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh" | |
| - name: Execute GitHub Copilot CLI | |
| id: agentic_execution | |
| # Copilot CLI tool arguments (sorted): | |
| timeout-minutes: 30 | |
| run: | | |
| set -o pipefail | |
| printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt | |
| trap 'rm -f "$HOME/.copilot/settings.json"' EXIT | |
| mkdir -p "$HOME/.copilot" | |
| printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json" | |
| export XDG_CONFIG_HOME="$HOME" | |
| export GH_AW_MCP_CONFIG="$HOME/.copilot/mcp-config.json" | |
| touch /tmp/gh-aw/agent-step-summary.md | |
| GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) | |
| export GH_AW_NODE_BIN | |
| export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" | |
| (umask 177 && touch /tmp/gh-aw/agent-stdio.log) | |
| GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-1000}" | |
| printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.7/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"*.githubusercontent.com\",\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"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\",\"patch-diff.githubusercontent.com\",\"ppa.launchpad.net\",\"raw.githubusercontent.com\",\"registry.npmjs.org\",\"s.symcb.com\",\"s.symcd.com\",\"security.ubuntu.com\",\"telemetry.enterprise.githubcopilot.com\",\"ts-crl.ws.symantec.com\",\"ts-ocsp.ws.symantec.com\",\"www.googleapis.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.5\",\"gpt-5.4\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.1\":[\"copilot/gpt-5.1*\",\"openai/gpt-5.1*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"image-generation\":[\"copilot/gpt-image*\",\"openai/gpt-image*\",\"openai/chatgpt-image*\",\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"google/imagen*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.27.7,squid=sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96,agent=sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c,api-proxy=sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6,cli-proxy=sha256:4757f198a3fa20f88bdbe70be7ae1a05f127d9c0a9e96a5d6460ef40c08fc83d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" | |
| cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json | |
| export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json" | |
| GH_AW_DOCKER_HOST="" | |
| if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then | |
| GH_AW_DOCKER_HOST="${DOCKER_HOST}" | |
| fi | |
| GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="" | |
| if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then | |
| GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw" | |
| python3 - <<'PY' | |
| import json,os,subprocess as sp | |
| from pathlib import Path | |
| try: | |
| p=Path(os.environ["RUNNER_TEMP"])/"gh-aw"/"awf-config.json" | |
| c=json.loads(p.read_text()) | |
| c["chroot"]={"binariesSourcePath":"/tmp/gh-aw","identity":{"user":sp.check_output(["id","-un"],text=True).strip(),"uid":int(sp.check_output(["id","-u"],text=True)),"gid":int(sp.check_output(["id","-g"],text=True)),"home":"/tmp/gh-aw/home"}} | |
| out=json.dumps(c,separators=(",",":"),ensure_ascii=False)+"\n" | |
| p.write_text(out) | |
| Path("/tmp/gh-aw/awf-config.json").write_text(out) | |
| except Exception as e: | |
| raise SystemExit(f"chroot config patch failed: {e}") from e | |
| PY | |
| fi | |
| GH_AW_TOOL_CACHE_MOUNT="" | |
| GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}" | |
| if [ -d "$GH_AW_TOOL_CACHE" ]; then | |
| if [[ "$GH_AW_TOOL_CACHE" != /opt/* ]]; then | |
| GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro" | |
| fi | |
| fi | |
| # shellcheck disable=SC1003,SC2086 | |
| 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" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_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 \ | |
| -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log | |
| env: | |
| AWF_REFLECT_ENABLED: 1 | |
| COPILOT_AGENT_RUNNER_TYPE: STANDALONE | |
| COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode | |
| COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} | |
| COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }} | |
| GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }} | |
| GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }} | |
| GH_AW_PHASE: agent | |
| GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt | |
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | |
| GH_AW_TIMEOUT_MINUTES: 30 | |
| GH_AW_VERSION: v0.80.9 | |
| GITHUB_API_URL: ${{ github.api_url }} | |
| GITHUB_AW: true | |
| GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows | |
| GITHUB_HEAD_REF: ${{ github.head_ref }} | |
| GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| GITHUB_REF_NAME: ${{ github.ref_name }} | |
| GITHUB_SERVER_URL: ${{ github.server_url }} | |
| GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md | |
| GITHUB_WORKSPACE: ${{ github.workspace }} | |
| GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com | |
| GIT_AUTHOR_NAME: github-actions[bot] | |
| GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com | |
| GIT_COMMITTER_NAME: github-actions[bot] | |
| RUNNER_TEMP: ${{ runner.temp }} | |
| TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }} | |
| - name: Detect agent errors | |
| if: always() | |
| id: detect-agent-errors | |
| continue-on-error: true | |
| run: node "${RUNNER_TEMP}/gh-aw/actions/detect_agent_errors.cjs" | |
| - name: Configure Git credentials | |
| env: | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| GITHUB_SERVER_URL: ${{ github.server_url }} | |
| GITHUB_TOKEN: ${{ github.token }} | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh" | |
| - name: Copy Copilot session state files to logs | |
| if: always() | |
| continue-on-error: true | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh" | |
| - name: Stop MCP Gateway | |
| if: always() | |
| continue-on-error: true | |
| env: | |
| MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} | |
| MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} | |
| GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} | |
| run: | | |
| bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID" | |
| - name: Redact secrets in logs | |
| if: always() | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/redact_secrets.cjs'); | |
| await main(); | |
| env: | |
| GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' | |
| SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} | |
| SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} | |
| SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} | |
| SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Append agent step summary | |
| if: always() | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh" | |
| - name: Copy Safe Outputs | |
| if: always() | |
| env: | |
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | |
| run: | | |
| mkdir -p /tmp/gh-aw | |
| cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true | |
| - name: Ingest agent output | |
| id: collect_output | |
| if: always() | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | |
| GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,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,patch-diff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" | |
| GITHUB_SERVER_URL: ${{ github.server_url }} | |
| GITHUB_API_URL: ${{ github.api_url }} | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/collect_ndjson_output.cjs'); | |
| await main(); | |
| - name: Parse agent logs for step summary | |
| if: always() | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_copilot_log.cjs'); | |
| await main(); | |
| - name: Parse MCP Gateway logs for step summary | |
| if: always() | |
| id: parse-mcp-gateway | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_mcp_gateway_log.cjs'); | |
| await main(); | |
| - name: Print firewall logs | |
| if: always() | |
| continue-on-error: true | |
| env: | |
| AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs | |
| run: | | |
| # Fix permissions on firewall logs/audit dirs so they can be uploaded as artifacts | |
| # AWF runs with sudo, creating files owned by root | |
| sudo chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true | |
| # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) | |
| if command -v awf &> /dev/null; then | |
| awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" | |
| else | |
| echo 'AWF binary not installed, skipping firewall log summary' | |
| fi | |
| - name: Parse token usage for step summary | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_token_usage.cjs'); | |
| await main(); | |
| - name: Print AWF reflect summary | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/awf_reflect_summary.cjs'); | |
| await main(); | |
| - name: Write agent output placeholder if missing | |
| if: always() | |
| run: | | |
| if [ ! -f /tmp/gh-aw/agent_output.json ]; then | |
| echo '{"items":[]}' > /tmp/gh-aw/agent_output.json | |
| fi | |
| - name: Upload agent artifacts | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: agent | |
| path: | | |
| /tmp/gh-aw/aw-prompts/prompt.txt | |
| /tmp/gh-aw/sandbox/agent/logs/ | |
| /tmp/gh-aw/redacted-urls.log | |
| /tmp/gh-aw/mcp-logs/ | |
| /tmp/gh-aw/proxy-logs/ | |
| !/tmp/gh-aw/proxy-logs/proxy-tls/ | |
| /tmp/gh-aw/agent_usage.json | |
| /tmp/gh-aw/agent-stdio.log | |
| /tmp/gh-aw/pre-agent-audit.txt | |
| /tmp/gh-aw/agent/ | |
| /tmp/gh-aw/github_rate_limits.jsonl | |
| /tmp/gh-aw/safeoutputs.jsonl | |
| /tmp/gh-aw/agent_output.json | |
| /tmp/gh-aw/aw-*.patch | |
| /tmp/gh-aw/aw-*.bundle | |
| /tmp/gh-aw/awf-config.json | |
| /tmp/gh-aw/sandbox/firewall/logs/ | |
| /tmp/gh-aw/sandbox/firewall/audit/ | |
| /tmp/gh-aw/sandbox/firewall/awf-reflect.json | |
| if-no-files-found: ignore | |
| apm: | |
| needs: | |
| - activation | |
| - apm-prep | |
| runs-on: ubuntu-slim | |
| strategy: | |
| fail-fast: false | |
| matrix: ${{ fromJSON(needs.apm-prep.outputs.matrix) }} | |
| permissions: | |
| {} | |
| env: | |
| AW_APM_APPS: ${{ github.aw.import-inputs.apps }} | |
| AW_APM_LEGACY_APP_ID: ${{ github.aw.import-inputs.app-id }} | |
| AW_APM_LEGACY_PRIVATE_KEY: ${{ github.aw.import-inputs.private-key }} | |
| steps: | |
| - name: Configure GH_HOST for enterprise compatibility | |
| id: ghes-host-config | |
| shell: bash | |
| run: | # zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input. | |
| # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct | |
| # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op. | |
| GH_HOST="${GITHUB_SERVER_URL#https://}" | |
| GH_HOST="${GH_HOST#http://}" | |
| echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV" | |
| - name: Resolve credentials for this matrix row | |
| if: ${{ matrix.group.has-app == 'true' }} | |
| run: | | |
| # SECURITY: never `set -x` or `echo "$pk"` in this step. ::add-mask:: | |
| # registers the PEM as a single multi-line substring; the masker will | |
| # not match individual PEM lines printed in isolation, so any future | |
| # debug echo of $pk line-by-line would leak the key body in clear text. | |
| set -euo pipefail | |
| case "$ROW_KIND" in | |
| legacy) | |
| app_id="${AW_APM_LEGACY_APP_ID:-}" | |
| pk="${AW_APM_LEGACY_PRIVATE_KEY:-}" | |
| ;; | |
| apps) | |
| apps_json="${AW_APM_APPS:-[]}" | |
| app_id=$(printf '%s' "$apps_json" | jq -r --argjson i "$ROW_INDEX" '.[$i]["app-id"] // ""') | |
| pk=$(printf '%s' "$apps_json" | jq -r --argjson i "$ROW_INDEX" '.[$i]["private-key"] // ""') | |
| ;; | |
| *) | |
| echo "::error::unexpected apm matrix kind '$ROW_KIND' for row with has-app=true" | |
| exit 1 | |
| ;; | |
| esac | |
| if [ -z "$app_id" ] || [ -z "$pk" ]; then | |
| echo "::error::missing app-id or private-key for apm row kind=$ROW_KIND index=$ROW_INDEX" | |
| exit 1 | |
| fi | |
| # Normalise trailing newline. Bash $(jq ...) strips ALL trailing | |
| # newlines from PEMs read out of the apps[] JSON, while a direct | |
| # env-var assignment preserves them. Stripping any tail and adding | |
| # exactly one makes the legacy and apps paths produce byte-identical | |
| # ROW_PRIVATE_KEY values so downstream tolerance is irrelevant. | |
| pk="${pk%$'\n'}" | |
| # Defence in depth: the PK is already masked because it came from | |
| # a secrets-context reference at compile time (gh-aw substitutes | |
| # the configured private-key secret into AW_APM_*), but | |
| # registering it again here makes the contract explicit and | |
| # survives any future gh-aw template churn that might lose the | |
| # secret tag. NOTE: do not write GitHub Actions expression syntax | |
| # (dollar-doublecurly ... doublecurly) inside this comment. | |
| # gh-aw v0.76+ harvests such tokens out of bash run-block bodies | |
| # (even inside `#` comments) and hoists them into the step env, | |
| # which fails workflow load when the inner expression resolves | |
| # to a sequence (e.g. wildcard secrets-context references). | |
| echo "::add-mask::$pk" | |
| # Use a random heredoc delimiter to eliminate any chance of a PEM | |
| # line collision terminating the value early. The official docs | |
| # explicitly warn against fixed delimiters for arbitrary multi-line | |
| # values: https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings | |
| delim="APMPK_$(openssl rand -hex 16)" | |
| { | |
| echo "ROW_APP_ID=$app_id" | |
| printf 'ROW_PRIVATE_KEY<<%s\n' "$delim" | |
| printf '%s\n' "$pk" | |
| printf '%s\n' "$delim" | |
| } >> "$GITHUB_ENV" | |
| env: | |
| ROW_INDEX: ${{ matrix.group.index }} | |
| ROW_KIND: ${{ matrix.group.kind }} | |
| - name: Mint installation token | |
| id: token | |
| if: ${{ matrix.group.has-app == 'true' }} | |
| uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 | |
| with: | |
| app-id: ${{ env.ROW_APP_ID }} | |
| owner: ${{ matrix.group.owner != '' && matrix.group.owner || github.repository_owner }} | |
| private-key: ${{ env.ROW_PRIVATE_KEY }} | |
| repositories: ${{ matrix.group.repositories }} | |
| - name: Render package list | |
| id: list | |
| run: | | |
| DEPS=$(echo "$AW_PKG" | jq -r '.[] | "- " + .') | |
| { | |
| echo "deps<<APMDEPS" | |
| printf '%s\n' "$DEPS" | |
| echo "APMDEPS" | |
| } >> "$GITHUB_OUTPUT" | |
| env: | |
| AW_PKG: ${{ toJSON(matrix.group.packages) }} | |
| - name: Pack APM packages | |
| id: pack | |
| uses: microsoft/apm-action@d723bb64ed70c135bbaf87d126b721dd2dae0439 # v1.10.0 | |
| env: | |
| GITHUB_TOKEN: ${{ steps.token.outputs.token || secrets.GH_AW_PLUGINS_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| with: | |
| apm-version: 0.21.0 | |
| archive: "true" | |
| dependencies: ${{ steps.list.outputs.deps }} | |
| isolated: "true" | |
| pack: "true" | |
| target: copilot | |
| working-directory: /tmp/gh-aw/apm-workspace | |
| - name: Upload APM bundle artifact | |
| if: success() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 (source v7) | |
| with: | |
| name: ${{ needs.activation.outputs.artifact_prefix }}apm-${{ matrix.group.id }} | |
| path: ${{ steps.pack.outputs.bundle-path }} | |
| retention-days: "1" | |
| apm-prep: | |
| needs: activation | |
| runs-on: ubuntu-slim | |
| permissions: | |
| {} | |
| outputs: | |
| matrix: ${{ steps.compute.outputs.matrix }} | |
| steps: | |
| - name: Configure GH_HOST for enterprise compatibility | |
| id: ghes-host-config | |
| shell: bash | |
| run: | # zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input. | |
| # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct | |
| # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op. | |
| GH_HOST="${GITHUB_SERVER_URL#https://}" | |
| GH_HOST="${GH_HOST#http://}" | |
| echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV" | |
| - name: Compute APM credential-group matrix | |
| id: compute | |
| run: | | |
| set -euo pipefail | |
| packages_json=${AW_APM_PACKAGES:-null} | |
| apps_json=${AW_APM_APPS:-null} | |
| legacy_id=${AW_APM_LEGACY_APP_ID:-} | |
| # gh-aw substitutes `["microsoft/apm#main"]` at | |
| # compile time using Go's default slice formatter, which emits | |
| # `[a b c]` (space-separated, no quotes) instead of valid JSON. | |
| # That breaks `jq --argjson` below. Repair string-array inputs | |
| # in place; leave already-valid JSON untouched. apps[] (objects) | |
| # is not repairable this way -- consumers must use the legacy | |
| # single-app inputs until upstream gh-aw exposes a JSON-encoding | |
| # helper for import-inputs. | |
| repair_string_array() { | |
| local raw="$1" | |
| if [ -z "$raw" ] || [ "$raw" = "null" ]; then | |
| echo "$raw"; return | |
| fi | |
| if printf '%s' "$raw" | jq -e 'type=="array"' >/dev/null 2>&1; then | |
| echo "$raw"; return | |
| fi | |
| python3 -c 'import json, re, sys; s=sys.argv[1].strip(); s=s[1:-1] if s.startswith("[") and s.endswith("]") else s; print(json.dumps([t for t in re.split(r"[\s,]+", s) if t]))' "$raw" | |
| } | |
| packages_json=$(repair_string_array "$packages_json") | |
| groups=$(jq -nc \ | |
| --argjson packages "$packages_json" \ | |
| --argjson apps "$apps_json" \ | |
| --arg legacy_id "$legacy_id" \ | |
| --arg legacy_owner "${AW_APM_LEGACY_OWNER:-}" \ | |
| --arg legacy_repos "${AW_APM_LEGACY_REPOS:-}" \ | |
| 'def slug(s): s | gsub("[^a-zA-Z0-9-]"; "-") | ascii_downcase | .[0:32]; | |
| def with_id(g): | |
| g + (if (g.id // "") == "" then {id: ("auto-" + slug(g.owner // "default"))} else {} end); | |
| [ | |
| (if (($packages // []) | length) > 0 and $legacy_id == "" | |
| then [{id:"default",kind:"default",index:0,owner:"",repositories:"",packages:$packages,("has-app"):"false"}] | |
| else [] end), | |
| (if $legacy_id != "" | |
| then [with_id({id:"legacy",kind:"legacy",index:0,owner:$legacy_owner,repositories:$legacy_repos,packages:($packages // []),("has-app"):"true"})] | |
| else [] end), | |
| (($apps // []) | to_entries | map( | |
| with_id({ | |
| id: (.value.id // ""), | |
| kind: "apps", | |
| index: .key, | |
| owner: (.value.owner // ""), | |
| repositories: (.value.repositories // ""), | |
| packages: (.value.packages // []), | |
| ("has-app"): "true" | |
| }))) | |
| ] | add // []') | |
| count=$(echo "$groups" | jq 'length') | |
| if [ "$count" = "0" ]; then | |
| echo "::error::shared/apm.md import provided no packages. Add packages: <list>, single-app inputs (app-id + private-key), or apps: <list> in the with: block." | |
| exit 1 | |
| fi | |
| dups=$(echo "$groups" | jq -r '[.[].id] | group_by(.) | map(select(length > 1) | first) | join(", ")') | |
| if [ -n "$dups" ]; then | |
| echo "::error::duplicate apm group ids after auto-derivation: $dups. Set apps[].id explicitly when two entries share the same owner." | |
| exit 1 | |
| fi | |
| while IFS= read -r id; do | |
| if ! echo "$id" | grep -Eq '^[a-z0-9-]{1,32}$'; then | |
| echo "::error::invalid apm group id: '$id' (lowercase alphanumeric and dashes, 1-32 chars). Set apps[].id explicitly." | |
| exit 1 | |
| fi | |
| done < <(echo "$groups" | jq -r '.[].id') | |
| # Emit only id + package-count to logs to keep notice output tight. | |
| # The matrix itself is non-secret under the env-relay design, but a | |
| # condensed summary remains easier to scan in the run UI. | |
| { | |
| echo "matrix={\"group\":$groups}" | |
| } >> "$GITHUB_OUTPUT" | |
| printf "::notice::APM matrix: %d credential group(s)\n" "$count" | |
| echo "$groups" | jq -r '.[] | " - " + .id + " (" + (.packages | length | tostring) + " package(s))"' | |
| env: | |
| AW_APM_APPS: ${{ github.aw.import-inputs.apps }} | |
| AW_APM_LEGACY_APP_ID: ${{ github.aw.import-inputs.app-id }} | |
| AW_APM_LEGACY_OWNER: ${{ github.aw.import-inputs.owner }} | |
| AW_APM_LEGACY_REPOS: ${{ github.aw.import-inputs.repositories }} | |
| AW_APM_PACKAGES: "[\"microsoft/apm#main\"]" | |
| conclusion: | |
| needs: | |
| - activation | |
| - agent | |
| - apm | |
| - apm-prep | |
| - detection | |
| - safe_outputs | |
| if: > | |
| always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' || | |
| needs.activation.outputs.stale_lock_file_failed == 'true' || needs.activation.outputs.daily_ai_credits_exceeded == 'true') | |
| runs-on: ubuntu-slim | |
| permissions: | |
| actions: write | |
| contents: read | |
| issues: write | |
| pull-requests: write | |
| concurrency: | |
| group: "gh-aw-conclusion-triage-panel" | |
| cancel-in-progress: false | |
| queue: max | |
| outputs: | |
| incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }} | |
| noop_message: ${{ steps.noop.outputs.noop_message }} | |
| tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} | |
| total_count: ${{ steps.missing_tool.outputs.total_count }} | |
| steps: | |
| - name: Setup Scripts | |
| id: setup | |
| uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9 | |
| with: | |
| destination: ${{ runner.temp }}/gh-aw/actions | |
| job-name: ${{ github.job }} | |
| trace-id: ${{ needs.activation.outputs.setup-trace-id }} | |
| parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} | |
| env: | |
| GH_AW_SETUP_WORKFLOW_NAME: "Triage Panel" | |
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/triage-panel.lock.yml@${{ github.ref }} | |
| GH_AW_INFO_VERSION: "1.0.63" | |
| GH_AW_INFO_AWF_VERSION: "v0.27.7" | |
| GH_AW_INFO_ENGINE_ID: "copilot" | |
| - name: Download agent output artifact | |
| id: download-agent-output | |
| continue-on-error: true | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: agent | |
| path: /tmp/gh-aw/ | |
| - name: Setup agent output environment variable | |
| id: setup-agent-output-env | |
| if: steps.download-agent-output.outcome == 'success' | |
| run: | | |
| mkdir -p /tmp/gh-aw/ | |
| find "/tmp/gh-aw/" -type f -print | |
| echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" | |
| - name: Collect usage artifact files | |
| if: always() | |
| continue-on-error: true | |
| run: | | |
| mkdir -p /tmp/gh-aw/usage/agent /tmp/gh-aw/usage/detection | |
| echo "Usage artifact source file status:" | |
| for file in /tmp/gh-aw/aw_info.json /tmp/gh-aw/aw-info.jsonl /tmp/gh-aw/agent_usage.jsonl /tmp/gh-aw/detection_usage.jsonl /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/threat-detection/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/threat-detection/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/threat-detection/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl; do | |
| [ -f "$file" ] && echo "FOUND: $file" || echo "MISSING: $file" | |
| done | |
| [ -f /tmp/gh-aw/aw_info.json ] && cp /tmp/gh-aw/aw_info.json /tmp/gh-aw/usage/aw_info.json || true | |
| [ -f /tmp/gh-aw/aw-info.jsonl ] && cp /tmp/gh-aw/aw-info.jsonl /tmp/gh-aw/usage/aw-info.jsonl || true | |
| [ -f /tmp/gh-aw/agent_usage.jsonl ] && cp /tmp/gh-aw/agent_usage.jsonl /tmp/gh-aw/usage/agent_usage.jsonl || true | |
| [ -f /tmp/gh-aw/detection_usage.jsonl ] && cp /tmp/gh-aw/detection_usage.jsonl /tmp/gh-aw/usage/detection_usage.jsonl || true | |
| [ -f /tmp/gh-aw/github_rate_limits.jsonl ] && cp /tmp/gh-aw/github_rate_limits.jsonl /tmp/gh-aw/usage/github_rate_limits.jsonl || true | |
| [ -f /tmp/gh-aw/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/agent/token_usage.jsonl || true | |
| [ -f /tmp/gh-aw/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/agent/token_usage.jsonl || true | |
| [ -f /tmp/gh-aw/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/agent/token_usage.jsonl || true | |
| [ -f /tmp/gh-aw/threat-detection/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/threat-detection/sandbox/firewall-audit-logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/detection/token_usage.jsonl || true | |
| [ -f /tmp/gh-aw/threat-detection/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/threat-detection/sandbox/firewall/logs/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/detection/token_usage.jsonl || true | |
| [ -f /tmp/gh-aw/threat-detection/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl ] && cp /tmp/gh-aw/threat-detection/sandbox/firewall/audit/api-proxy-logs/token-usage.jsonl /tmp/gh-aw/usage/detection/token_usage.jsonl || true | |
| [ -f /tmp/gh-aw/usage/agent/token_usage.jsonl ] || : > /tmp/gh-aw/usage/agent/token_usage.jsonl | |
| [ -f /tmp/gh-aw/usage/detection/token_usage.jsonl ] || : > /tmp/gh-aw/usage/detection/token_usage.jsonl | |
| mkdir -p /tmp/gh-aw/usage/activity | |
| node ${{ runner.temp }}/gh-aw/actions/generate_usage_activity_summary.cjs | |
| find /tmp/gh-aw/usage -type f -print | sort | |
| - name: Upload usage artifact | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: usage | |
| path: | | |
| /tmp/gh-aw/usage/aw_info.json | |
| /tmp/gh-aw/usage/aw-info.jsonl | |
| /tmp/gh-aw/usage/agent_usage.jsonl | |
| /tmp/gh-aw/usage/detection_usage.jsonl | |
| /tmp/gh-aw/usage/github_rate_limits.jsonl | |
| /tmp/gh-aw/usage/agent/token_usage.jsonl | |
| /tmp/gh-aw/usage/detection/token_usage.jsonl | |
| /tmp/gh-aw/usage/activity/summary.json | |
| if-no-files-found: ignore | |
| - name: Restore daily AIC usage cache | |
| id: restore-daily-aic-cache-conclusion | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 | |
| with: | |
| key: agentic-workflow-usage-triagepanel-${{ github.run_id }} | |
| restore-keys: agentic-workflow-usage-triagepanel- | |
| path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl | |
| - name: Write daily AIC usage cache entry | |
| id: write-daily-aic-cache | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| github-token: ${{ github.token }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/write_daily_aic_usage_cache.cjs'); | |
| await main(); | |
| - name: Save daily AIC usage cache | |
| id: save-daily-aic-cache | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 | |
| with: | |
| key: agentic-workflow-usage-triagepanel-${{ github.run_id }} | |
| path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl | |
| - name: Upload daily AIC usage cache artifact | |
| id: upload-daily-aic-cache | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: aic-usage-cache | |
| path: /tmp/gh-aw/agentic-workflow-usage-cache.jsonl | |
| if-no-files-found: ignore | |
| retention-days: 7 | |
| - name: Process no-op messages | |
| id: noop | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | |
| GH_AW_NOOP_MAX: "1" | |
| GH_AW_WORKFLOW_NAME: "Triage Panel" | |
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/triage-panel.md" | |
| GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} | |
| GH_AW_NOOP_REPORT_AS_ISSUE: "true" | |
| GH_AW_AIC: ${{ needs.agent.outputs.aic }} | |
| GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }} | |
| GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }} | |
| GH_AW_WORKFLOW_ID: "triage-panel" | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs'); | |
| await main(); | |
| - name: Log detection run | |
| id: detection_runs | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | |
| GH_AW_WORKFLOW_NAME: "Triage Panel" | |
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/triage-panel.md" | |
| GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }} | |
| GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_detection_runs.cjs'); | |
| await main(); | |
| - name: Record missing tool | |
| id: missing_tool | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | |
| GH_AW_MISSING_TOOL_CREATE_ISSUE: "true" | |
| GH_AW_WORKFLOW_NAME: "Triage Panel" | |
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/triage-panel.md" | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs'); | |
| await main(); | |
| - name: Record incomplete | |
| id: report_incomplete | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | |
| GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true" | |
| GH_AW_WORKFLOW_NAME: "Triage Panel" | |
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/triage-panel.md" | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/report_incomplete_handler.cjs'); | |
| await main(); | |
| - name: Handle agent failure | |
| id: handle_agent_failure | |
| if: always() | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | |
| GH_AW_WORKFLOW_NAME: "Triage Panel" | |
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/triage-panel.md" | |
| GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} | |
| GH_AW_WORKFLOW_ID: "triage-panel" | |
| GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168" | |
| GH_AW_ENGINE_ID: "copilot" | |
| GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} | |
| GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} | |
| GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }} | |
| GH_AW_AI_CREDITS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.ai_credits_rate_limit_error || 'false' }} | |
| GH_AW_UNKNOWN_MODEL_AI_CREDITS: ${{ needs.agent.outputs.unknown_model_ai_credits || 'false' }} | |
| GH_AW_AIC: ${{ needs.agent.outputs.aic }} | |
| GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }} | |
| GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }} | |
| GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} | |
| GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }} | |
| GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }} | |
| GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }} | |
| GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com" | |
| GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }} | |
| GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }} | |
| GH_AW_DAILY_AI_CREDITS_EXCEEDED: ${{ needs.activation.outputs.daily_ai_credits_exceeded }} | |
| GH_AW_DAILY_AI_CREDITS_TOTAL_EFFECTIVE_TOKENS: ${{ needs.activation.outputs.daily_ai_credits_total_effective_tokens }} | |
| GH_AW_DAILY_AI_CREDITS_THRESHOLD: ${{ needs.activation.outputs.daily_ai_credits_threshold }} | |
| GH_AW_GROUP_REPORTS: "false" | |
| GH_AW_FAILURE_REPORT_AS_ISSUE: "true" | |
| GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true" | |
| GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true" | |
| GH_AW_TIMEOUT_MINUTES: "30" | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs'); | |
| await main(); | |
| detection: | |
| needs: | |
| - activation | |
| - agent | |
| if: > | |
| always() && needs.agent.result != 'skipped' && (needs.agent.outputs.output_types != '' || needs.agent.outputs.has_patch == 'true') | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| outputs: | |
| aic: ${{ steps.parse_detection_token_usage.outputs.aic }} | |
| detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }} | |
| detection_reason: ${{ steps.detection_conclusion.outputs.reason }} | |
| detection_success: ${{ steps.detection_conclusion.outputs.success }} | |
| steps: | |
| - name: Setup Scripts | |
| id: setup | |
| uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9 | |
| with: | |
| destination: ${{ runner.temp }}/gh-aw/actions | |
| job-name: ${{ github.job }} | |
| trace-id: ${{ needs.activation.outputs.setup-trace-id }} | |
| parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} | |
| env: | |
| GH_AW_SETUP_WORKFLOW_NAME: "Triage Panel" | |
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/triage-panel.lock.yml@${{ github.ref }} | |
| GH_AW_INFO_VERSION: "1.0.63" | |
| GH_AW_INFO_AWF_VERSION: "v0.27.7" | |
| GH_AW_INFO_ENGINE_ID: "copilot" | |
| - name: Download agent output artifact | |
| id: download-agent-output | |
| continue-on-error: true | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: agent | |
| path: /tmp/gh-aw/ | |
| - name: Setup agent output environment variable | |
| id: setup-agent-output-env | |
| if: steps.download-agent-output.outcome == 'success' | |
| run: | | |
| mkdir -p /tmp/gh-aw/ | |
| find "/tmp/gh-aw/" -type f -print | |
| echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" | |
| - name: Checkout repository for patch context | |
| if: needs.agent.outputs.has_patch == 'true' | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| persist-credentials: false | |
| # --- Threat Detection --- | |
| - name: Clean stale firewall files from agent artifact | |
| run: | | |
| rm -rf /tmp/gh-aw/sandbox/firewall/logs | |
| rm -rf /tmp/gh-aw/sandbox/firewall/audit | |
| - name: Download container images | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.27.7@sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c ghcr.io/github/gh-aw-firewall/api-proxy:0.27.7@sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6 ghcr.io/github/gh-aw-firewall/squid:0.27.7@sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96 | |
| - name: Check if detection needed | |
| id: detection_guard | |
| if: always() | |
| env: | |
| OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }} | |
| HAS_PATCH: ${{ needs.agent.outputs.has_patch }} | |
| run: | | |
| if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then | |
| echo "run_detection=true" >> "$GITHUB_OUTPUT" | |
| echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH" | |
| else | |
| echo "run_detection=false" >> "$GITHUB_OUTPUT" | |
| echo "Detection skipped: no agent outputs or patches to analyze" | |
| fi | |
| - name: Clear MCP Config for detection | |
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | |
| run: | | |
| rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json" | |
| rm -f "$HOME/.copilot/mcp-config.json" | |
| rm -f "$GITHUB_WORKSPACE/.gemini/settings.json" | |
| - name: Prepare threat detection files | |
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | |
| run: | | |
| mkdir -p /tmp/gh-aw/threat-detection/aw-prompts | |
| rm -f /tmp/gh-aw/agent_usage.json | |
| cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true | |
| if [ ! -s /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt ]; then | |
| echo "::warning::ERR_VALIDATION: Missing or empty detection context prompt at /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt. Ensure the agent artifact includes /tmp/gh-aw/aw-prompts/prompt.txt. Detection will continue with fallback workflow context." | |
| fi | |
| cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true | |
| for f in /tmp/gh-aw/aw-*.patch; do | |
| [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true | |
| done | |
| for f in /tmp/gh-aw/aw-*.bundle; do | |
| [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true | |
| done | |
| echo "Prepared threat detection files:" | |
| ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true | |
| - name: Setup threat detection | |
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| WORKFLOW_NAME: "Triage Panel" | |
| WORKFLOW_DESCRIPTION: "Auto-invoke the apm-triage-panel skill on a daily sweep of untriaged issues plus an opt-in fast path for explicit re-triage. Posts one synthesized verdict per issue and applies the panel-decided labels and milestone, with explicit \"agentic proposal pending human ratification\" framing." | |
| HAS_PATCH: ${{ needs.agent.outputs.has_patch }} | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/setup_threat_detection.cjs'); | |
| await main(); | |
| - name: Ensure threat-detection directory and log | |
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | |
| run: | | |
| mkdir -p /tmp/gh-aw/threat-detection | |
| touch /tmp/gh-aw/threat-detection/detection.log | |
| - name: Setup Node.js | |
| uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | |
| with: | |
| node-version: '24' | |
| package-manager-cache: false | |
| - name: Install GitHub Copilot CLI | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.63 | |
| env: | |
| GH_HOST: github.com | |
| - name: Install AWF binary | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.27.7 | |
| - name: Execute GitHub Copilot CLI | |
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | |
| continue-on-error: true | |
| id: detection_agentic_execution | |
| # Copilot CLI tool arguments (sorted): | |
| timeout-minutes: 20 | |
| run: | | |
| set -o pipefail | |
| printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt | |
| trap 'rm -f "$HOME/.copilot/settings.json"' EXIT | |
| mkdir -p "$HOME/.copilot" | |
| printf '%s' '{"builtInAgents":{"rubberDuck":false}}' > "$HOME/.copilot/settings.json" | |
| export XDG_CONFIG_HOME="$HOME" | |
| touch /tmp/gh-aw/agent-step-summary.md | |
| GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) | |
| export GH_AW_NODE_BIN | |
| export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" | |
| (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log) | |
| GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-400}" | |
| printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.7/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"github.com\",\"host.docker.internal\",\"registry.npmjs.org\",\"telemetry.enterprise.githubcopilot.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5},\"container\":{\"imageTag\":\"0.27.7,squid=sha256:deb1d4e19de62d51cee0508057a596a19315c3423ada4d675cad136dc8037c96,agent=sha256:aae231e4635c8999d039c132f1602d3df850fe9b84a00aa2b5ac981179b5661c,api-proxy=sha256:009caf2e3d88fa77b64e9a03a95a228fc58db0f1701c6d324b29ba5a3c7c79b6,cli-proxy=sha256:4757f198a3fa20f88bdbe70be7ae1a05f127d9c0a9e96a5d6460ef40c08fc83d\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" | |
| cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json | |
| export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json" | |
| GH_AW_DOCKER_HOST="" | |
| if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then | |
| GH_AW_DOCKER_HOST="${DOCKER_HOST}" | |
| fi | |
| GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="" | |
| if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then | |
| GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw" | |
| _GH_AW_CHROOT_JSON=$(jq -c --arg src /tmp/gh-aw --arg user "$(id -un)" --argjson uid "$(id -u)" --argjson gid "$(id -g)" --arg home /tmp/gh-aw/home '.chroot={"binariesSourcePath":$src,"identity":{"user":$user,"uid":$uid,"gid":$gid,"home":$home}}' "${RUNNER_TEMP}/gh-aw/awf-config.json") || { echo "chroot config patch failed" >&2; exit 1; } | |
| printf '%s\n' "$_GH_AW_CHROOT_JSON" > "${RUNNER_TEMP}/gh-aw/awf-config.json" | |
| printf '%s\n' "$_GH_AW_CHROOT_JSON" > "/tmp/gh-aw/awf-config.json" | |
| fi | |
| GH_AW_TOOL_CACHE_MOUNT="" | |
| GH_AW_TOOL_CACHE="${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}" | |
| if [ -d "$GH_AW_TOOL_CACHE" ]; then | |
| if [[ "$GH_AW_TOOL_CACHE" != /opt/* ]]; then | |
| GH_AW_TOOL_CACHE_MOUNT="$GH_AW_TOOL_CACHE:$GH_AW_TOOL_CACHE:ro" | |
| fi | |
| fi | |
| # shellcheck disable=SC1003,SC2086 | |
| 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" ${GH_AW_TOOL_CACHE_MOUNT:+--mount "$GH_AW_TOOL_CACHE_MOUNT"} ${GH_AW_DOCKER_HOST:+--docker-host "$GH_AW_DOCKER_HOST"} ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --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 'set +o histexpand; : "${RUNNER_TOOL_CACHE:?RUNNER_TOOL_CACHE must be set}"; GH_AW_TOOL_CACHE="$RUNNER_TOOL_CACHE"; export PATH="$(find "$GH_AW_TOOL_CACHE" -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; GH_AW_NPM_GLOBAL_ROOT="$(npm root -g 2>/dev/null || true)"; if [ -n "$GH_AW_NPM_GLOBAL_ROOT" ]; then export NODE_PATH="${GH_AW_NPM_GLOBAL_ROOT}${NODE_PATH:+:${NODE_PATH}}"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log | |
| env: | |
| AWF_REFLECT_ENABLED: 1 | |
| COPILOT_AGENT_RUNNER_TYPE: STANDALONE | |
| COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode | |
| COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} | |
| COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'claude-sonnet-4.6' }} | |
| GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_DETECTION_MAX_AI_CREDITS || '400' }} | |
| GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }} | |
| GH_AW_PHASE: detection | |
| GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt | |
| GH_AW_TIMEOUT_MINUTES: 20 | |
| GH_AW_VERSION: v0.80.9 | |
| GITHUB_API_URL: ${{ github.api_url }} | |
| GITHUB_AW: true | |
| GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows | |
| GITHUB_HEAD_REF: ${{ github.head_ref }} | |
| GITHUB_REF_NAME: ${{ github.ref_name }} | |
| GITHUB_SERVER_URL: ${{ github.server_url }} | |
| GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md | |
| GITHUB_WORKSPACE: ${{ github.workspace }} | |
| GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com | |
| GIT_AUTHOR_NAME: github-actions[bot] | |
| GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com | |
| GIT_COMMITTER_NAME: github-actions[bot] | |
| RUNNER_TEMP: ${{ runner.temp }} | |
| TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }} | |
| - name: Parse threat detection token usage for step summary | |
| id: parse_detection_token_usage | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_TOKEN_USAGE_SUMMARY_TITLE: Threat Detection Token Usage | |
| with: | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_token_usage.cjs'); | |
| await main(); | |
| - name: Upload threat detection log | |
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: detection | |
| path: /tmp/gh-aw/threat-detection/detection.log | |
| if-no-files-found: ignore | |
| - name: Parse and conclude threat detection | |
| id: detection_conclusion | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} | |
| DETECTION_AGENTIC_EXECUTION_OUTCOME: ${{ steps.detection_agentic_execution.outcome }} | |
| GH_AW_DETECTION_CONTINUE_ON_ERROR: "true" | |
| with: | |
| script: | | |
| try { | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_threat_detection_results.cjs'); | |
| await main(); | |
| } catch (loadErr) { | |
| const continueOnError = process.env.GH_AW_DETECTION_CONTINUE_ON_ERROR !== 'false'; | |
| const detectionExecutionFailed = process.env.DETECTION_AGENTIC_EXECUTION_OUTCOME === 'failure'; | |
| const msg = 'ERR_SYSTEM: \u274C Unexpected error loading threat detection module: ' + (loadErr && loadErr.message ? loadErr.message : String(loadErr)); | |
| core.error(msg); | |
| core.setOutput('reason', 'parse_error'); | |
| if (continueOnError && !detectionExecutionFailed) { | |
| core.warning('\u26A0\uFE0F ' + msg); | |
| core.setOutput('conclusion', 'warning'); | |
| core.setOutput('success', 'false'); | |
| } else { | |
| core.setOutput('conclusion', 'failure'); | |
| core.setOutput('success', 'false'); | |
| core.setFailed(msg); | |
| } | |
| } | |
| pre_activation: | |
| if: > | |
| github.event_name != 'issues' | |
| || (github.event.label.name == 'status/needs-triage' | |
| && github.event.issue.user.type != 'Bot' | |
| && github.event.issue.locked != true | |
| && github.event.issue.state == 'open') | |
| runs-on: ubuntu-slim | |
| outputs: | |
| activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} | |
| matched_command: '' | |
| setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }} | |
| setup-span-id: ${{ steps.setup.outputs.span-id }} | |
| setup-trace-id: ${{ steps.setup.outputs.trace-id }} | |
| steps: | |
| - name: Setup Scripts | |
| id: setup | |
| uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9 | |
| with: | |
| destination: ${{ runner.temp }}/gh-aw/actions | |
| job-name: ${{ github.job }} | |
| env: | |
| GH_AW_SETUP_WORKFLOW_NAME: "Triage Panel" | |
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/triage-panel.lock.yml@${{ github.ref }} | |
| GH_AW_INFO_VERSION: "1.0.63" | |
| GH_AW_INFO_AWF_VERSION: "v0.27.7" | |
| GH_AW_INFO_ENGINE_ID: "copilot" | |
| - name: Check team membership for workflow | |
| id: check_membership | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_REQUIRED_ROLES: "admin,maintainer,write" | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/check_membership.cjs'); | |
| await main(); | |
| safe_outputs: | |
| needs: | |
| - activation | |
| - agent | |
| - detection | |
| if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' | |
| runs-on: ubuntu-slim | |
| permissions: | |
| actions: write | |
| contents: read | |
| issues: write | |
| pull-requests: write | |
| timeout-minutes: 45 | |
| env: | |
| GH_AW_AGENT_AIC: ${{ needs.agent.outputs.aic }} | |
| GH_AW_AIC: ${{ needs.agent.outputs.aic }} | |
| GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }} | |
| GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/triage-panel" | |
| GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }} | |
| GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }} | |
| GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }} | |
| GH_AW_ENGINE_ID: "copilot" | |
| GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }} | |
| GH_AW_ENGINE_VERSION: "1.0.63" | |
| GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }} | |
| GH_AW_WORKFLOW_ID: "triage-panel" | |
| GH_AW_WORKFLOW_NAME: "Triage Panel" | |
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/triage-panel.md" | |
| outputs: | |
| assign_milestone_milestone_assigned: ${{ steps.process_safe_outputs.outputs.milestone_assigned }} | |
| code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} | |
| code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} | |
| comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }} | |
| comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }} | |
| create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} | |
| create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} | |
| process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} | |
| process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} | |
| steps: | |
| - name: Setup Scripts | |
| id: setup | |
| uses: github/gh-aw-actions/setup@8c7d04ebf1ece56cd381446125da3e0f6896294a # v0.80.9 | |
| with: | |
| destination: ${{ runner.temp }}/gh-aw/actions | |
| job-name: ${{ github.job }} | |
| trace-id: ${{ needs.activation.outputs.setup-trace-id }} | |
| parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }} | |
| env: | |
| GH_AW_SETUP_WORKFLOW_NAME: "Triage Panel" | |
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/triage-panel.lock.yml@${{ github.ref }} | |
| GH_AW_INFO_VERSION: "1.0.63" | |
| GH_AW_INFO_AWF_VERSION: "v0.27.7" | |
| GH_AW_INFO_ENGINE_ID: "copilot" | |
| - name: Download agent output artifact | |
| id: download-agent-output | |
| continue-on-error: true | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: agent | |
| path: /tmp/gh-aw/ | |
| - name: Setup agent output environment variable | |
| id: setup-agent-output-env | |
| if: steps.download-agent-output.outcome == 'success' | |
| run: | | |
| mkdir -p /tmp/gh-aw/ | |
| find "/tmp/gh-aw/" -type f -print | |
| echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" | |
| - name: Configure GH_HOST for enterprise compatibility | |
| id: ghes-host-config | |
| shell: bash | |
| run: | # zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input. | |
| # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct | |
| # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op. | |
| GH_HOST="${GITHUB_SERVER_URL#https://}" | |
| GH_HOST="${GH_HOST#http://}" | |
| echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV" | |
| - name: Process Safe Outputs | |
| id: process_safe_outputs | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} | |
| GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }} | |
| GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,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,patch-diff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" | |
| GITHUB_SERVER_URL: ${{ github.server_url }} | |
| GITHUB_API_URL: ${{ github.api_url }} | |
| GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":12},\"add_labels\":{\"allowed\":[\"theme/governance\",\"theme/portability\",\"theme/security\",\"area/audit-policy\",\"area/ci-cd\",\"area/cli\",\"area/content-security\",\"area/distribution\",\"area/docs-site\",\"area/enterprise\",\"area/lockfile\",\"area/marketplace\",\"area/mcp-config\",\"area/mcp-trust\",\"area/multi-target\",\"area/package-authoring\",\"area/testing\",\"type/architecture\",\"type/automation\",\"type/bug\",\"type/docs\",\"type/feature\",\"type/performance\",\"type/refactor\",\"type/release\",\"priority/high\",\"priority/low\",\"status/accepted\",\"status/blocked\",\"status/in-flight\",\"status/needs-design\",\"status/triaged\",\"good first issue\",\"help wanted\",\"test/triage-validation\"],\"max\":70,\"target\":\"*\"},\"assign_milestone\":{\"max\":12},\"create_report_incomplete_issue\":{},\"dispatch_workflow\":{\"max\":10,\"workflow_files\":{\"project-sync\":\".yml\"},\"workflows\":[\"project-sync\"]},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"remove_labels\":{\"allowed\":[\"status/needs-triage\"],\"max\":12,\"target\":\"*\"},\"report_incomplete\":{}}" | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require('${{ runner.temp }}/gh-aw/actions/safe_output_handler_manager.cjs'); | |
| await main(); | |
| - name: Upload Safe Outputs Items | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: safe-outputs-items | |
| path: | | |
| /tmp/gh-aw/safe-output-items.jsonl | |
| /tmp/gh-aw/temporary-id-map.json | |
| if-no-files-found: ignore | |