AI Issue Triage #720
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":"32dafff82d91dc3dd686cc31b03037df3f80c4b9e0eda6dce72284b2603d4438","body_hash":"6888a8616b1e836e084e3cd296ca09ccf40e28cfe6b7d9b09ec734319ea2faba","compiler_version":"v0.87.10","strict":true,"agent_id":"copilot","agent_model":"small","engine_versions":{"copilot":"1.0.80"}} | |
| # gh-aw-manifest: {"version":1,"secrets":["GH_AW_DEFAULT_OTLP_HEADERS","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/cache/restore","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/cache/save","sha":"55cc8345863c7cc4c66a329aec7e433d2d1c52a9","version":"v6.1.0"},{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-python","sha":"5fda3b95a4ea91299a34e894583c3862153e4b97","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"bc8c008a419c5b7a29df6f5641edd35fd1c6ea85","version":"v0.87.10"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.28.10","digest":"sha256:c01e6d16d11ea4f2a46cc023a9f402224a3b3861b026818eec0dc586d7e6918e","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.28.10@sha256:c01e6d16d11ea4f2a46cc023a9f402224a3b3861b026818eec0dc586d7e6918e"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.10","digest":"sha256:c3a18aebb8251339117ea998296315de17bada366f8d03919b3348ea71112e64","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.10@sha256:c3a18aebb8251339117ea998296315de17bada366f8d03919b3348ea71112e64"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.28.10","digest":"sha256:c06076f7aca95df713e0748c44d80c0a3c2538fad67bfdd04296d45158e083e6","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.28.10@sha256:c06076f7aca95df713e0748c44d80c0a3c2538fad67bfdd04296d45158e083e6"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.14","digest":"sha256:b2f0c2b2f17b5fbe809e5bb99dc185b6ddd70df25295dc63a6d526350334eff5","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.14@sha256:b2f0c2b2f17b5fbe809e5bb99dc185b6ddd70df25295dc63a6d526350334eff5"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:bac2192f6374d6262116399b34fc5e143d576f82719e90a18261cae7480f4d4e","pinned_image":"ghcr.io/github/gh-aw-node@sha256:bac2192f6374d6262116399b34fc5e143d576f82719e90a18261cae7480f4d4e"}],"mcp_servers":[{"name":"safeoutputs","tools":["missing_data","missing_tool","noop","publish_triage_summary"]}]} | |
| # This file was automatically generated by gh-aw (v0.87.10). 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/ | |
| # | |
| # Maintain one concise issue summary with likely duplicates and missing-information guidance. | |
| # | |
| # Secrets used: | |
| # - GH_AW_DEFAULT_OTLP_HEADERS | |
| # - GH_AW_GITHUB_MCP_SERVER_TOKEN | |
| # - GH_AW_GITHUB_TOKEN | |
| # - GITHUB_TOKEN | |
| # | |
| # Custom actions used: | |
| # - actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| # - actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| # - actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| # - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| # - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| # - actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| # - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| # - github/gh-aw-actions/setup@bc8c008a419c5b7a29df6f5641edd35fd1c6ea85 # v0.87.10 | |
| # | |
| # Container images used: | |
| # - ghcr.io/github/gh-aw-firewall/agent:0.28.10@sha256:c01e6d16d11ea4f2a46cc023a9f402224a3b3861b026818eec0dc586d7e6918e | |
| # - ghcr.io/github/gh-aw-firewall/api-proxy:0.28.10@sha256:c3a18aebb8251339117ea998296315de17bada366f8d03919b3348ea71112e64 | |
| # - ghcr.io/github/gh-aw-firewall/squid:0.28.10@sha256:c06076f7aca95df713e0748c44d80c0a3c2538fad67bfdd04296d45158e083e6 | |
| # - ghcr.io/github/gh-aw-mcpg:v0.4.14@sha256:b2f0c2b2f17b5fbe809e5bb99dc185b6ddd70df25295dc63a6d526350334eff5 | |
| # - ghcr.io/github/gh-aw-node@sha256:bac2192f6374d6262116399b34fc5e143d576f82719e90a18261cae7480f4d4e | |
| name: "AI Issue Triage" | |
| on: | |
| issues: | |
| types: | |
| - opened | |
| - edited | |
| # roles: all # Roles processed as role check in pre-activation job | |
| permissions: {} | |
| concurrency: | |
| cancel-in-progress: true | |
| group: issue-triage-${{ github.event.issue.number }} | |
| run-name: "AI Issue Triage" | |
| env: | |
| OTEL_EXPORTER_OTLP_ENDPOINT: ${{ vars.GH_AW_DEFAULT_OTLP_ENDPOINT }} | |
| OTEL_SERVICE_NAME: gh-aw.issue-triage | |
| OTEL_RESOURCE_ATTRIBUTES: 'gh-aw.workflow.name=AI%20Issue%20Triage,gh-aw.repository=${{ github.repository }},gh-aw.run.id=${{ github.run_id }},github.run_id=${{ github.run_id }},gh-aw.engine.id=copilot' | |
| OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.GH_AW_DEFAULT_OTLP_HEADERS }} | |
| GH_AW_OTLP_ENDPOINTS: '[{"url":"${{ vars.GH_AW_DEFAULT_OTLP_ENDPOINT }}","headers":"${{ secrets.GH_AW_DEFAULT_OTLP_HEADERS }}"}]' | |
| GH_AW_OTLP_IF_MISSING: ignore | |
| jobs: | |
| activation: | |
| needs: pre_activation | |
| if: needs.pre_activation.outputs.activated == 'true' | |
| runs-on: ubuntu-slim | |
| permissions: | |
| actions: read | |
| contents: read | |
| env: | |
| GH_AW_MAX_DAILY_AI_CREDITS: "300" | |
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | |
| 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_guardrail_status: ${{ steps.daily-effective-workflow-guardrail.outputs.daily_ai_credits_guardrail_status || '' }} | |
| 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 }} | |
| oauth_token_check_failed: ${{ steps.check-oauth-tokens.outputs.oauth_token_check_failed == 'true' }} | |
| 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@bc8c008a419c5b7a29df6f5641edd35fd1c6ea85 # v0.87.10 | |
| 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: "AI Issue Triage" | |
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-triage.lock.yml@${{ github.ref }} | |
| GH_AW_INFO_VERSION: "1.0.80" | |
| GH_AW_INFO_AWF_VERSION: "v0.28.10" | |
| GH_AW_INFO_ENGINE_ID: "copilot" | |
| - name: Mask OTLP telemetry headers | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" | |
| - 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: "small" | |
| GH_AW_INFO_VERSION: "1.0.80" | |
| GH_AW_INFO_AGENT_VERSION: "1.0.80" | |
| GH_AW_INFO_CLI_VERSION: "v0.87.10" | |
| GH_AW_INFO_WORKFLOW_NAME: "AI Issue Triage" | |
| GH_AW_INFO_EXPERIMENTAL: "false" | |
| GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" | |
| GH_AW_INFO_STAGED: "false" | |
| GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]' | |
| GH_AW_INFO_FIREWALL_ENABLED: "true" | |
| GH_AW_INFO_AWF_VERSION: "v0.28.10" | |
| GH_AW_INFO_AWMG_VERSION: "" | |
| GH_AW_INFO_FIREWALL_TYPE: "squid" | |
| GH_AW_INFO_AGENT_RUNTIME: "" | |
| GH_AW_INFO_FRONTMATTER_EMOJI: "📌" | |
| GH_AW_COMPILED_STRICT: "true" | |
| GH_AW_INFO_FEATURES: '{"issue-intents":true}' | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, '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@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| key: agentic-workflow-usage-issuetriage-${{ github.run_id }} | |
| restore-keys: agentic-workflow-usage-issuetriage- | |
| 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 path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, '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: "AI Issue Triage" | |
| GH_AW_WORKFLOW_ID: "issue-triage" | |
| 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: "300" | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, 'check_daily_aic_workflow_guardrail.cjs')); | |
| await main(); | |
| - name: Check for OAuth tokens | |
| id: check-oauth-tokens | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/check_oauth_tokens.sh" | |
| env: | |
| GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} | |
| GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} | |
| - name: Checkout .github and .agents folders | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| sparse-checkout: | | |
| .github | |
| .agents | |
| .claude | |
| .codex | |
| .gemini | |
| .pi | |
| sparse-checkout-cone-mode: true | |
| fetch-depth: 1 | |
| - name: Save agent config folders for base branch restoration | |
| env: | |
| GH_AW_AGENT_FOLDERS: ".agents .github" | |
| GH_AW_AGENT_FILES: "AGENTS.md" | |
| 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: "issue-triage.lock.yml" | |
| GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}" | |
| with: | |
| script: | | |
| const path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, '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.87.10" | |
| with: | |
| script: | | |
| const path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, '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: "api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,s.symcb.com,s.symcd.com,security.ubuntu.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com" | |
| with: | |
| script: | | |
| const path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, 'compute_text.cjs')); | |
| await main(); | |
| - name: Log runtime features | |
| if: ${{ contains(toJSON(vars), '"GH_AW_RUNTIME_FEATURES":') }} | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/log_runtime_features_summary.sh" | |
| - name: Create prompt with built-in context | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_ACTIONS_DIR: ${{ runner.temp }}/gh-aw/actions | |
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | |
| GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl | |
| GH_AW_PROMPT_CONFIG: "{\"items\":[{\"content_env\":\"GH_AW_PROMPT_CONTENT_0000\"},{\"file\":\"xpia.md\"},{\"file\":\"temp_folder_prompt.md\"},{\"file\":\"markdown.md\"},{\"file\":\"safe_outputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0001\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0002\"},{\"file\":\"mcp_cli_tools_with_safeoutputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0003\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0004\"}]}" | |
| GH_AW_PROMPT_CONTENT_0000: "<system>\n" | |
| GH_AW_PROMPT_CONTENT_0001: "<safe-output-tools>\nTools: missing_tool, missing_data, noop, publish_triage_summary\n" | |
| GH_AW_PROMPT_CONTENT_0002: "</safe-output-tools>\n" | |
| GH_AW_PROMPT_CONTENT_0003: "</system>\n" | |
| GH_AW_PROMPT_CONTENT_0004: "{{#runtime-import .github/workflows/issue-triage.md}}\n" | |
| with: | |
| script: | | |
| const { setupGlobals } = require(process.env.GH_AW_ACTIONS_DIR + '/setup_globals.cjs'); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(process.env.GH_AW_ACTIONS_DIR + '/create_prompt.cjs'); | |
| await main(core); | |
| - name: Interpolate variables and render templates | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | |
| GH_AW_ENGINE_ID: "copilot" | |
| with: | |
| script: | | |
| const path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, 'interpolate_prompt.cjs')); | |
| await main(); | |
| - name: Substitute placeholders | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | |
| 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 path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const substitutePlaceholders = require(path.join(actionsDir, 'substitute_placeholders.cjs')); | |
| // Call the substitution function | |
| return await substitutePlaceholders({ | |
| file: process.env.GH_AW_PROMPT, | |
| substitutions: { | |
| 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: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | |
| run: | | |
| bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh" | |
| - name: Print prompt | |
| env: | |
| GH_AW_PROMPT: ${{ runner.temp }}/gh-aw/aw-prompts/prompt.txt | |
| run: | | |
| bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh" | |
| - name: Stage prompt files for artifact upload | |
| run: | | |
| mkdir -p /tmp/gh-aw/aw-prompts | |
| cp -a "${RUNNER_TEMP}/gh-aw/aw-prompts/." /tmp/gh-aw/aw-prompts/ | |
| - name: Upload activation artifact | |
| if: success() || failure() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| 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 | |
| if: needs.activation.outputs.daily_ai_credits_exceeded != 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| copilot-requests: write | |
| issues: read | |
| timeout-minutes: 60 | |
| 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_PR_HEAD_BASE_BRANCH: "" | |
| GH_AW_PR_HEAD_BASE_PR_NUMBER: "" | |
| GH_AW_PR_HEAD_BASE_REF: "" | |
| GH_AW_PR_HEAD_BASE_REPO: "" | |
| GH_AW_PR_HEAD_BASE_SHA: "" | |
| GH_AW_PR_HEAD_REPO: "" | |
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | |
| GH_AW_WORKFLOW_ID_SANITIZED: issuetriage | |
| 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 }} | |
| http_400_response_error: ${{ steps.detect-agent-errors.outputs.http_400_response_error || 'false' }} | |
| inference_access_error: ${{ steps.detect-agent-errors.outputs.inference_access_error || 'false' }} | |
| invocation_cap_exceeded: ${{ steps.detect-agent-errors.outputs.invocation_cap_exceeded || 'false' }} | |
| max_cache_misses_exceeded: ${{ steps.detect-agent-errors.outputs.max_cache_misses_exceeded || 'false' }} | |
| mcp_policy_error: ${{ steps.detect-agent-errors.outputs.mcp_policy_error || 'false' }} | |
| missing_model_pricing_error: ${{ steps.detect-agent-errors.outputs.missing_model_pricing_error || 'false' }} | |
| missing_model_pricing_model_name: ${{ steps.detect-agent-errors.outputs.missing_model_pricing_model_name || '' }} | |
| 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 }} | |
| shell_expansion_guard_rejected: ${{ steps.detect-agent-errors.outputs.shell_expansion_guard_rejected || 'false' }} | |
| 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@bc8c008a419c5b7a29df6f5641edd35fd1c6ea85 # v0.87.10 | |
| 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: "AI Issue Triage" | |
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-triage.lock.yml@${{ github.ref }} | |
| GH_AW_INFO_VERSION: "1.0.80" | |
| GH_AW_INFO_AWF_VERSION: "v0.28.10" | |
| GH_AW_INFO_ENGINE_ID: "copilot" | |
| - name: Set runtime paths | |
| id: set-runtime-paths | |
| run: | | |
| if [ -z "${RUNNER_TOOL_CACHE:-}" ]; then | |
| echo "RUNNER_TOOL_CACHE=${{ runner.tool_cache }}" >> "$GITHUB_ENV" | |
| fi | |
| { | |
| 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: Mask OTLP telemetry headers | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" | |
| - name: Check OTLP telemetry configuration | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/check_otlp_default_credentials.sh" | |
| - name: Checkout repository | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| persist-credentials: false | |
| - name: Setup Python | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: '3.12' | |
| - 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 activation artifact | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: activation | |
| path: /tmp/gh-aw | |
| - env: | |
| GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl | |
| GITHUB_TOKEN: ${{ github.token }} | |
| id: prepare | |
| name: Prepare deterministic issue evidence | |
| run: python .github/scripts/issue-triage/issue-context.py "$GITHUB_EVENT_PATH" "/tmp/gh-aw/issue-context.md" "/tmp/gh-aw/triage-event.json" | |
| - if: steps.prepare.outputs.should_process == 'true' | |
| name: Prepare sanitized bug report context | |
| run: python .github/scripts/issue-triage/bug-report-analyzer.py "/tmp/gh-aw/triage-event.json" "/tmp/gh-aw/bug-report-context.md" | |
| - 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 path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, 'checkout_pr_branch.cjs')); | |
| await main(); | |
| - name: Install GitHub Copilot CLI | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" | |
| env: | |
| GH_HOST: github.com | |
| GH_AW_COMPILED_VERSION: v0.87.10 | |
| - name: Install AWF binary | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.28.10 --rootless | |
| - name: Restore agent config folders from base branch | |
| if: steps.checkout-pr.outcome == 'success' | |
| env: | |
| GH_AW_AGENT_FOLDERS: ".agents .github" | |
| GH_AW_AGENT_FILES: "AGENTS.md" | |
| 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.28.10@sha256:c01e6d16d11ea4f2a46cc023a9f402224a3b3861b026818eec0dc586d7e6918e ghcr.io/github/gh-aw-firewall/api-proxy:0.28.10@sha256:c3a18aebb8251339117ea998296315de17bada366f8d03919b3348ea71112e64 ghcr.io/github/gh-aw-firewall/squid:0.28.10@sha256:c06076f7aca95df713e0748c44d80c0a3c2538fad67bfdd04296d45158e083e6 ghcr.io/github/gh-aw-mcpg:v0.4.14@sha256:b2f0c2b2f17b5fbe809e5bb99dc185b6ddd70df25295dc63a6d526350334eff5 ghcr.io/github/gh-aw-node@sha256:bac2192f6374d6262116399b34fc5e143d576f82719e90a18261cae7480f4d4e | |
| - name: Prepare Safe Outputs Directories | |
| run: | | |
| mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" | |
| mkdir -p /tmp/gh-aw/safeoutputs | |
| mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs | |
| - name: Generate Safe Outputs Config | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_FILE_ROOT: "${{ runner.temp }}/gh-aw" | |
| GH_AW_FILE_CONFIG: "{\"files\":[{\"path\":\"safeoutputs/config.json\",\"content_env\":\"GH_AW_SAFE_OUTPUTS_CONFIG\"}]}" | |
| GH_AW_SAFE_OUTPUTS_CONFIG: "{\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"publish-triage-summary\":{\"description\":\"Create or update the canonical triage summary for the triggering issue.\",\"inputs\":{\"bug_report_confidence\":{\"default\":null,\"description\":\"Confidence in the diagnostic findings.\",\"options\":[\"HIGH\",\"MEDIUM\",\"LOW\",\"NONE\"],\"required\":true,\"type\":\"choice\"},\"bug_report_findings\":{\"default\":null,\"description\":\"Concise evidence-based diagnostic findings, or a short status explanation.\",\"required\":true,\"type\":\"string\"},\"bug_report_requirement\":{\"default\":null,\"description\":\"Whether a diagnostic report is required, recommended, optional, or not applicable.\",\"options\":[\"REQUIRED\",\"RECOMMENDED\",\"OPTIONAL\",\"NOT_APPLICABLE\"],\"required\":true,\"type\":\"choice\"},\"bug_report_status\":{\"default\":null,\"description\":\"Processing status copied from the sanitized bug report context.\",\"options\":[\"ANALYZED\",\"NOT_FOUND\",\"REJECTED\",\"NOT_APPLICABLE\"],\"required\":true,\"type\":\"choice\"},\"duplicate_candidates_json\":{\"default\":null,\"description\":\"JSON array of up to five objects with integer number, short reason, and HIGH/MEDIUM/LOW confidence fields, or [].\",\"required\":true,\"type\":\"string\"},\"has_missing_information\":{\"default\":null,\"description\":\"Whether important information needed to investigate the issue is missing.\",\"required\":true,\"type\":\"boolean\"},\"input_sha256\":{\"default\":null,\"description\":\"The exact Input SHA-256 value copied from the deterministic issue evidence.\",\"required\":true,\"type\":\"string\"},\"issue_kind\":{\"default\":null,\"description\":\"BUG when the issue follows the PowerToys bug-report template, otherwise OTHER.\",\"options\":[\"BUG\",\"OTHER\"],\"required\":true,\"type\":\"choice\"},\"issue_language\":{\"default\":null,\"description\":\"Whether the author-written issue title and description are English.\",\"options\":[\"ENGLISH\",\"NON_ENGLISH\",\"UNCERTAIN\"],\"required\":true,\"type\":\"choice\"},\"missing_information\":{\"default\":null,\"description\":\"A concise sentence naming only the important missing information, or None.\",\"required\":true,\"type\":\"string\"},\"powertoys_version\":{\"default\":null,\"description\":\"The normalized PowerToys version from the bug template, or Not provided.\",\"required\":true,\"type\":\"string\"},\"product_label\":{\"default\":null,\"description\":\"The exact existing Product-* label matching the issue area, or None.\",\"required\":true,\"type\":\"string\"},\"reproduction_quality\":{\"default\":null,\"description\":\"Whether bug reproduction steps are sufficient for investigation.\",\"options\":[\"SUFFICIENT\",\"INSUFFICIENT\",\"NOT_APPLICABLE\"],\"required\":true,\"type\":\"choice\"},\"suggested_area\":{\"default\":null,\"description\":\"The most likely PowerToys product area or Unknown when unclear.\",\"required\":true,\"type\":\"string\"},\"summary\":{\"default\":null,\"description\":\"A concise one- or two-sentence summary of the reported problem or request.\",\"required\":true,\"type\":\"string\"}},\"output\":\"The canonical triage summary was published.\"},\"report_incomplete\":{}}" | |
| with: | |
| script: | | |
| const path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, 'create_files.cjs')); | |
| await main(); | |
| - name: Generate Safe Outputs Tools | |
| env: | |
| GH_AW_TOOLS_META_JSON: | | |
| { | |
| "description_suffixes": {}, | |
| "repo_params": {}, | |
| "dynamic_tools": [ | |
| { | |
| "description": "Create or update the canonical triage summary for the triggering issue.", | |
| "inputSchema": { | |
| "additionalProperties": false, | |
| "properties": { | |
| "bug_report_confidence": { | |
| "description": "Confidence in the diagnostic findings.", | |
| "enum": [ | |
| "HIGH", | |
| "MEDIUM", | |
| "LOW", | |
| "NONE" | |
| ], | |
| "type": "string" | |
| }, | |
| "bug_report_findings": { | |
| "description": "Concise evidence-based diagnostic findings, or a short status explanation.", | |
| "type": "string" | |
| }, | |
| "bug_report_requirement": { | |
| "description": "Whether a diagnostic report is required, recommended, optional, or not applicable.", | |
| "enum": [ | |
| "REQUIRED", | |
| "RECOMMENDED", | |
| "OPTIONAL", | |
| "NOT_APPLICABLE" | |
| ], | |
| "type": "string" | |
| }, | |
| "bug_report_status": { | |
| "description": "Processing status copied from the sanitized bug report context.", | |
| "enum": [ | |
| "ANALYZED", | |
| "NOT_FOUND", | |
| "REJECTED", | |
| "NOT_APPLICABLE" | |
| ], | |
| "type": "string" | |
| }, | |
| "duplicate_candidates_json": { | |
| "description": "JSON array of up to five objects with integer number, short reason, and HIGH/MEDIUM/LOW confidence fields, or [].", | |
| "type": "string" | |
| }, | |
| "has_missing_information": { | |
| "description": "Whether important information needed to investigate the issue is missing.", | |
| "type": "boolean" | |
| }, | |
| "input_sha256": { | |
| "description": "The exact Input SHA-256 value copied from the deterministic issue evidence.", | |
| "type": "string" | |
| }, | |
| "issue_kind": { | |
| "description": "BUG when the issue follows the PowerToys bug-report template, otherwise OTHER.", | |
| "enum": [ | |
| "BUG", | |
| "OTHER" | |
| ], | |
| "type": "string" | |
| }, | |
| "issue_language": { | |
| "description": "Whether the author-written issue title and description are English.", | |
| "enum": [ | |
| "ENGLISH", | |
| "NON_ENGLISH", | |
| "UNCERTAIN" | |
| ], | |
| "type": "string" | |
| }, | |
| "missing_information": { | |
| "description": "A concise sentence naming only the important missing information, or None.", | |
| "type": "string" | |
| }, | |
| "powertoys_version": { | |
| "description": "The normalized PowerToys version from the bug template, or Not provided.", | |
| "type": "string" | |
| }, | |
| "product_label": { | |
| "description": "The exact existing Product-* label matching the issue area, or None.", | |
| "type": "string" | |
| }, | |
| "reproduction_quality": { | |
| "description": "Whether bug reproduction steps are sufficient for investigation.", | |
| "enum": [ | |
| "SUFFICIENT", | |
| "INSUFFICIENT", | |
| "NOT_APPLICABLE" | |
| ], | |
| "type": "string" | |
| }, | |
| "suggested_area": { | |
| "description": "The most likely PowerToys product area or Unknown when unclear.", | |
| "type": "string" | |
| }, | |
| "summary": { | |
| "description": "A concise one- or two-sentence summary of the reported problem or request.", | |
| "type": "string" | |
| } | |
| }, | |
| "required": [ | |
| "bug_report_confidence", | |
| "bug_report_findings", | |
| "bug_report_requirement", | |
| "bug_report_status", | |
| "duplicate_candidates_json", | |
| "has_missing_information", | |
| "input_sha256", | |
| "issue_kind", | |
| "issue_language", | |
| "missing_information", | |
| "powertoys_version", | |
| "product_label", | |
| "reproduction_quality", | |
| "suggested_area", | |
| "summary" | |
| ], | |
| "type": "object" | |
| }, | |
| "name": "publish_triage_summary" | |
| } | |
| ] | |
| } | |
| GH_AW_VALIDATION_JSON: | | |
| { | |
| "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 | |
| } | |
| } | |
| }, | |
| "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 path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, 'generate_safe_outputs_tools.cjs')); | |
| await main(); | |
| - name: Start MCP Gateway | |
| id: start-mcp-gateway | |
| env: | |
| GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST: ${{ vars.GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST || 'true' }} | |
| 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_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -eo pipefail | |
| mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config" | |
| if [ -n "${GITHUB_EVENT_PATH:-}" ] && [ -r "${GITHUB_EVENT_PATH}" ]; then | |
| GH_AW_SAFEOUTPUTS_EVENT_PATH="${RUNNER_TEMP}/gh-aw/safeoutputs/github_event.json" | |
| cp "${GITHUB_EVENT_PATH}" "${GH_AW_SAFEOUTPUTS_EVENT_PATH}" | |
| export GITHUB_EVENT_PATH="${GH_AW_SAFEOUTPUTS_EVENT_PATH}" | |
| fi | |
| # Export gateway environment variables for MCP config and gateway script | |
| export MCP_GATEWAY_PORT="8080" | |
| export MCP_GATEWAY_DOMAIN="awmg-mcpg" | |
| export MCP_GATEWAY_HOST_DOMAIN="localhost" | |
| MCP_GATEWAY_AGENT_ID=$(openssl rand -base64 45 | tr -d '/+=') | |
| echo "::add-mask::${MCP_GATEWAY_AGENT_ID}" | |
| export MCP_GATEWAY_AGENT_ID | |
| export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" | |
| mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" | |
| export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" | |
| export MCP_GATEWAY_ALLOWED_MOUNT_ROOTS="${GITHUB_WORKSPACE}:rw,${RUNNER_TEMP}/gh-aw:ro,${RUNNER_TEMP}/gh-aw/safeoutputs:rw,/opt:ro,/tmp:rw,/usr/bin/gh:ro" | |
| export GH_AW_PR_HEAD_BASE_BRANCH="${GH_AW_PR_HEAD_BASE_BRANCH:-}" | |
| export GH_AW_PR_HEAD_BASE_SHA="${GH_AW_PR_HEAD_BASE_SHA:-}" | |
| export GH_AW_PR_HEAD_BASE_REPO="${GH_AW_PR_HEAD_BASE_REPO:-}" | |
| export GH_AW_PR_HEAD_BASE_PR_NUMBER="${GH_AW_PR_HEAD_BASE_PR_NUMBER:-}" | |
| export GH_AW_PR_HEAD_BASE_REF="${GH_AW_PR_HEAD_BASE_REF:-}" | |
| export GH_AW_PR_HEAD_REPO="${GH_AW_PR_HEAD_REPO:-}" | |
| export DEBUG="*" | |
| export GH_AW_ENGINE="copilot" | |
| export GH_AW_MCP_CLI_SERVERS='["safeoutputs"]' | |
| MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0') | |
| MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0') | |
| source "${RUNNER_TEMP}/gh-aw/actions/resolve_docker_socket_gid.sh" | |
| export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network bridge -p 127.0.0.1:'"${MCP_GATEWAY_PORT}"':'"${MCP_GATEWAY_PORT}"' --name awmg-mcpg --add-host host.docker.internal:host-gateway --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_AGENT_ID -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_PR_HEAD_BASE_BRANCH -e GH_AW_PR_HEAD_BASE_SHA -e GH_AW_PR_HEAD_BASE_REPO -e GH_AW_PR_HEAD_BASE_PR_NUMBER -e GH_AW_PR_HEAD_BASE_REF -e GH_AW_PR_HEAD_REPO -e GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST -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 GH_AW_SINK_VISIBILITY -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 -e RUNNER_TOOL_CACHE -e MCP_GATEWAY_ALLOWED_MOUNT_ROOTS -e GITHUB_AW_OTEL_TRACE_ID -e GITHUB_AW_OTEL_PARENT_SPAN_ID -e OTEL_EXPORTER_OTLP_HEADERS -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.4.14' | |
| 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_763bc2030deabb7d_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" | |
| { | |
| "mcpServers": { | |
| "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}", | |
| "GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST": "\${GH_AW_POLICY_ALLOW_CREATE_PULL_REQUEST}", | |
| "GH_AW_PR_HEAD_BASE_BRANCH": "\${GH_AW_PR_HEAD_BASE_BRANCH}", | |
| "GH_AW_PR_HEAD_BASE_SHA": "\${GH_AW_PR_HEAD_BASE_SHA}", | |
| "GH_AW_PR_HEAD_BASE_REPO": "\${GH_AW_PR_HEAD_BASE_REPO}", | |
| "GH_AW_PR_HEAD_BASE_PR_NUMBER": "\${GH_AW_PR_HEAD_BASE_PR_NUMBER}", | |
| "GH_AW_PR_HEAD_BASE_REF": "\${GH_AW_PR_HEAD_BASE_REF}", | |
| "GH_AW_PR_HEAD_REPO": "\${GH_AW_PR_HEAD_REPO}", | |
| "GITHUB_EVENT_NAME": "\${GITHUB_EVENT_NAME}", | |
| "GITHUB_EVENT_PATH": "\${GITHUB_EVENT_PATH}", | |
| "GITHUB_REPOSITORY": "\${GITHUB_REPOSITORY}", | |
| "GITHUB_SHA": "\${GITHUB_SHA}", | |
| "GITHUB_TOKEN": "\${GITHUB_TOKEN}", | |
| "GITHUB_WORKSPACE": "\${GITHUB_WORKSPACE}", | |
| "RUNNER_TEMP": "\${RUNNER_TEMP}" | |
| } | |
| } | |
| }, | |
| "gateway": { | |
| "port": $MCP_GATEWAY_PORT, | |
| "domain": "${MCP_GATEWAY_DOMAIN}", | |
| "agentId": "${MCP_GATEWAY_AGENT_ID}", | |
| "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}", | |
| "startupTimeout": 120, | |
| "opentelemetry": { | |
| "endpoint": "${OTEL_EXPORTER_OTLP_ENDPOINT}", | |
| "traceId": "${GITHUB_AW_OTEL_TRACE_ID}", | |
| "spanId": "${GITHUB_AW_OTEL_PARENT_SPAN_ID}" | |
| } | |
| } | |
| } | |
| GH_AW_MCP_CONFIG_763bc2030deabb7d_EOF | |
| - name: Mount MCP servers as CLIs | |
| id: mount-mcp-clis | |
| continue-on-error: true | |
| env: | |
| MCP_GATEWAY_AGENT_ID: ${{ steps.start-mcp-gateway.outputs.gateway-agent-id }} | |
| 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 path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io); | |
| const { main } = require(path.join(actionsDir, '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): | |
| # --allow-tool safeoutputs | |
| # --allow-tool shell(cat) | |
| # --allow-tool shell(date) | |
| # --allow-tool shell(echo) | |
| # --allow-tool shell(grep) | |
| # --allow-tool shell(head) | |
| # --allow-tool shell(ls) | |
| # --allow-tool shell(printf) | |
| # --allow-tool shell(pwd) | |
| # --allow-tool shell(safeoutputs) | |
| # --allow-tool shell(safeoutputs:*) | |
| # --allow-tool shell(sort) | |
| # --allow-tool shell(tail) | |
| # --allow-tool shell(uniq) | |
| # --allow-tool shell(wc) | |
| # --allow-tool shell(yq) | |
| timeout-minutes: ${{ fromJSON(vars.GH_AW_DEFAULT_TIMEOUT_MINUTES || '20') }} | |
| run: | | |
| set -o pipefail | |
| printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt | |
| trap 'gh_aw_exit_code=$?; mkdir -p /tmp/gh-aw >/dev/null 2>&1 || true; printf "%s" "$gh_aw_exit_code" > /tmp/gh-aw/agent_execution_exit_code.txt || true; rm -f "$HOME/.copilot/settings.json"; if [ "$gh_aw_exit_code" -ne 0 ]; then echo "::error::Agent execution exited with code $gh_aw_exit_code"; fi' 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" | |
| GH_AW_COPILOT_SRC="$(command -v copilot 2>/dev/null || true)" | |
| if [ -z "$GH_AW_COPILOT_SRC" ] || [ ! -x "$GH_AW_COPILOT_SRC" ]; then | |
| echo "GitHub Copilot CLI executable not found on PATH after installation" >&2 | |
| exit 127 | |
| fi | |
| GH_AW_COPILOT_BIN="${RUNNER_TEMP}/gh-aw/bin/copilot" | |
| mkdir -p "${RUNNER_TEMP}/gh-aw/bin" | |
| if [ "$GH_AW_COPILOT_SRC" != "$GH_AW_COPILOT_BIN" ]; then | |
| cp "$GH_AW_COPILOT_SRC" "$GH_AW_COPILOT_BIN" | |
| fi | |
| chmod 755 "$GH_AW_COPILOT_BIN" | |
| 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) | |
| # shellcheck disable=SC2016 | |
| printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.28.10/awf-config.schema.json","network":{"allowDomains":["api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.com","crl.geotrust.com","crl.globalsign.com","crl.identrust.com","crl.sectigo.com","crl.thawte.com","crl.usertrust.com","crl.verisign.com","crl3.digicert.com","crl4.digicert.com","crls.ssl.com","json-schema.org","json.schemastore.org","keyserver.ubuntu.com","ocsp.digicert.com","ocsp.geotrust.com","ocsp.globalsign.com","ocsp.identrust.com","ocsp.sectigo.com","ocsp.ssl.com","ocsp.thawte.com","ocsp.usertrust.com","ocsp.verisign.com","packagecloud.io","packages.cloud.google.com","packages.microsoft.com","ppa.launchpad.net","s.symcb.com","s.symcd.com","security.ubuntu.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com"],"isolation":true,"topologyAttach":["awmg-mcpg"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":5,"maxCacheMisses":5,"maxAiCredits":10,"models":{"agent":["sonnet-6x","gpt-5.4","gpt-5.5","gpt-5.6","gpt-5.3","gemini-pro","any"],"antigravity":["copilot/antigravity*","google/antigravity*","gemini/antigravity*"],"any":["copilot/*","anthropic/*","openai/*","google/*","gemini/*"],"auto":["copilot/auto","large"],"claude":["agent"],"codex":["agent"],"coding":["copilot/gpt-5*codex*","openai/gpt-5*codex*","gpt-5-codex","kimi"],"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*"],"detection":["small"],"evals":["small"],"fable":["copilot/*fable*","anthropic/*fable*"],"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-3.6-flash":["copilot/gemini-3.6*flash*","google/gemini-3.6*flash*","gemini/gemini-3.6*flash*"],"gemini-3.7-flash":["copilot/gemini-3.7*flash*","google/gemini-3.7*flash*","gemini/gemini-3.7*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-omni":["copilot/gemini-omni*","google/gemini-omni*","gemini/gemini-omni*"],"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*"],"gpt-5.6":["copilot/gpt-5.6*","openai/gpt-5.6*"],"grok":["copilot/*grok*","openai/*grok*"],"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*"],"kimi":["copilot/kimi*","openai/kimi*"],"kiwi":["copilot/kiwi*","openai/kiwi*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"lyria":["google/lyria*","gemini/lyria*","copilot/lyria*"],"mai-code":["copilot/MAI-Code*","copilot/mai-code*","openai/MAI-Code*"],"mai-code-1-flash-picker":["copilot/MAI-Code-1-Flash-picker*","copilot/mai-code-1-flash-picker*","openai/MAI-Code-1-Flash-picker*"],"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"],"raptor-mini":["copilot/raptor*","openai/raptor*"],"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-5*","copilot/*sonnet-4-5-*","anthropic/*sonnet-4-5-*","copilot/*sonnet-4-6*","anthropic/*sonnet-4-6*","anthropic/*sonnet-5*"],"summarization":["haiku","gpt-5-mini","gemini-flash-lite","mini"],"veo":["google/veo*","gemini/veo*"],"vision":["copilot/gemini-*image*","google/gemini-*image*","gemini/gemini-*image*","copilot/gemini-*flash*","google/gemini-*flash*","gemini/gemini-*flash*"]}},"container":{"imageTag":"0.28.10,squid=sha256:c06076f7aca95df713e0748c44d80c0a3c2538fad67bfdd04296d45158e083e6,agent=sha256:c01e6d16d11ea4f2a46cc023a9f402224a3b3861b026818eec0dc586d7e6918e,api-proxy=sha256:c3a18aebb8251339117ea998296315de17bada366f8d03919b3348ea71112e64,cli-proxy=sha256:a61070cb7f21840c5f2ec74d55b49adf0652d0348ce059015aaaca33a8cb6b45"},"logging":{"proxyLogsDir":"/tmp/gh-aw/sandbox/firewall/logs","auditDir":"/tmp/gh-aw/sandbox/firewall/audit"}}' > "${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 | |
| if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then | |
| GH_AW_CHROOT_BINARIES_SOURCE_PATH="${RUNNER_TEMP}/gh-aw" GH_AW_CHROOT_IDENTITY_HOME="${RUNNER_TEMP}/gh-aw/home" node "${RUNNER_TEMP}/gh-aw/actions/patch_awf_chroot_config.cjs" | |
| 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,SC2016,SC2086 | |
| GH_AW_AWF_ENGINE_NAME=copilot \ | |
| GH_AW_AWF_HARNESS_MARKER='[copilot-harness]' \ | |
| GH_AW_AWF_LOG_FILE=/tmp/gh-aw/agent-stdio.log \ | |
| GH_AW_AWF_ATTEMPT_LOG_NAME=copilot \ | |
| bash "${RUNNER_TEMP}/gh-aw/actions/run_awf_with_startup_retries.sh" -- \ | |
| 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"} --env-all --exclude-env ACTIONS_ID_TOKEN_REQUEST_TOKEN --exclude-env ACTIONS_ID_TOKEN_REQUEST_URL --exclude-env COPILOT_GITHUB_TOKEN --exclude-env MCP_GATEWAY_AGENT_ID --mount /tmp/gh-aw:/tmp/gh-aw:rw --log-level info --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; [ -n "$ERLANG_HOME" ] && export PATH="$ERLANG_HOME/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" "${RUNNER_TEMP}/gh-aw/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-tool safeoutputs --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(printf)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(safeoutputs)'\'' --allow-tool '\''shell(safeoutputs:*)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --deny-tool write --deny-tool '\''shell(cat)'\'' --deny-tool '\''shell(date)'\'' --deny-tool '\''shell(echo)'\'' --deny-tool '\''shell(grep)'\'' --deny-tool '\''shell(head)'\'' --deny-tool '\''shell(ls)'\'' --deny-tool '\''shell(printf)'\'' --deny-tool '\''shell(pwd)'\'' --deny-tool '\''shell(sort)'\'' --deny-tool '\''shell(tail)'\'' --deny-tool '\''shell(uniq)'\'' --deny-tool '\''shell(wc)'\'' --deny-tool '\''shell(yq)'\'' --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' | |
| env: | |
| AWF_REFLECT_ENABLED: 1 | |
| COPILOT_AGENT_RUNNER_TYPE: STANDALONE | |
| COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode | |
| COPILOT_GITHUB_TOKEN: ${{ github.token }} | |
| COPILOT_MODEL: small | |
| GH_AW_LLM_PROVIDER: github | |
| GH_AW_MAX_TURNS: 5 | |
| 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: ${{ fromJSON(vars.GH_AW_DEFAULT_TIMEOUT_MINUTES || '20') }} | |
| GH_AW_VERSION: v0.87.10 | |
| 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 }} | |
| S2STOKENS: true | |
| 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 | |
| env: | |
| GH_AW_AGENTIC_EXECUTION_OUTCOME: ${{ steps.agentic_execution.outcome }} | |
| GH_AW_ENGINE_STEP_TIMEOUT_MINUTES: ${{ fromJSON(vars.GH_AW_DEFAULT_TIMEOUT_MINUTES || '20') }} | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, 'detect_agent_errors.cjs')); | |
| await main(); | |
| - 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_AGENT_ID: ${{ steps.start-mcp-gateway.outputs.gateway-agent-id }} | |
| 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 path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, 'redact_secrets.cjs')); | |
| await main(); | |
| env: | |
| GH_AW_SECRET_NAMES: 'GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,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: "api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,s.symcb.com,s.symcd.com,security.ubuntu.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 path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, '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/ | |
| GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} | |
| with: | |
| script: | | |
| const path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, '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 path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, '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: bash "${RUNNER_TEMP}/gh-aw/actions/print_firewall_logs.sh" --rootless | |
| - name: Parse token usage for step summary | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, '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 path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, 'awf_reflect_summary.cjs')); | |
| await main(); | |
| - name: Generate observability summary | |
| if: always() | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, 'generate_observability_summary.cjs')); | |
| await main(core); | |
| - 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 | |
| # Small dedicated copy of the agent output so safe-output processing | |
| # survives a failed or timed-out upload of the larger agent artifact | |
| - name: Upload agent output fallback artifact | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: agent-output-fallback | |
| path: | | |
| /tmp/gh-aw/agent_output.json | |
| /tmp/gh-aw/safeoutputs.jsonl | |
| if-no-files-found: ignore | |
| - name: Upload agent artifacts | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| 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/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/otel.jsonl | |
| /tmp/gh-aw/otlp-export-errors.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 | |
| conclusion: | |
| needs: | |
| - activation | |
| - agent | |
| - detection | |
| - publish_triage_summary | |
| - safe_outputs | |
| if: > | |
| always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' || | |
| needs.activation.outputs.oauth_token_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 | |
| issues: write | |
| concurrency: | |
| group: "gh-aw-conclusion-issue-triage" | |
| cancel-in-progress: false | |
| queue: max | |
| env: | |
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | |
| 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@bc8c008a419c5b7a29df6f5641edd35fd1c6ea85 # v0.87.10 | |
| 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: "AI Issue Triage" | |
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-triage.lock.yml@${{ github.ref }} | |
| GH_AW_INFO_VERSION: "1.0.80" | |
| GH_AW_INFO_AWF_VERSION: "v0.28.10" | |
| 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: | |
| pattern: "{agent,agent-output-fallback}" | |
| merge-multiple: true | |
| 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 | |
| if [ -f "/tmp/gh-aw/agent_output.json" ]; then | |
| echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Download detection artifact | |
| id: download-detection-artifact | |
| continue-on-error: true | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: detection | |
| path: /tmp/gh-aw/threat-detection/ | |
| - name: Download Safe Outputs Items Manifest | |
| id: download-safe-outputs-manifest | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| pattern: safe-outputs-items | |
| merge-multiple: true | |
| path: /tmp/gh-aw/ | |
| - name: Collect usage artifact files | |
| if: always() | |
| continue-on-error: true | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/collect_usage_artifact_files.sh" | |
| - name: Upload usage artifact | |
| if: always() | |
| continue-on-error: true | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: usage | |
| path: | | |
| /tmp/gh-aw/usage/aw_info.json | |
| /tmp/gh-aw/usage/aw-info.jsonl | |
| /tmp/gh-aw/usage/agent_usage.json | |
| /tmp/gh-aw/usage/agent_usage.jsonl | |
| /tmp/gh-aw/usage/detection_usage.jsonl | |
| /tmp/gh-aw/usage/evals.jsonl | |
| /tmp/gh-aw/usage/graders/grader_manifest.json | |
| /tmp/gh-aw/usage/graders/grader_results.json | |
| /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@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| key: agentic-workflow-usage-issuetriage-${{ github.run_id }} | |
| restore-keys: agentic-workflow-usage-issuetriage- | |
| 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 path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context); | |
| const { main } = require(path.join(actionsDir, '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@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| key: agentic-workflow-usage-issuetriage-${{ 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.0.1 | |
| 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: "AI Issue Triage" | |
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/issue-triage.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: "false" | |
| 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: "issue-triage" | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, '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: "AI Issue Triage" | |
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/issue-triage.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 path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, '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: "AI Issue Triage" | |
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/issue-triage.md" | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, '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: "false" | |
| GH_AW_REPORT_INCOMPLETE_TITLE_PREFIX: "[incomplete]" | |
| GH_AW_WORKFLOW_NAME: "AI Issue Triage" | |
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/issue-triage.md" | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, '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: "AI Issue Triage" | |
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/issue-triage.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: "issue-triage" | |
| GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "0" | |
| GH_AW_ENGINE_ID: "copilot" | |
| 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: "10" | |
| 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_HTTP_400_RESPONSE_ERROR: ${{ needs.agent.outputs.http_400_response_error }} | |
| GH_AW_MAX_CACHE_MISSES_EXCEEDED: ${{ needs.agent.outputs.max_cache_misses_exceeded }} | |
| GH_AW_MISSING_MODEL_PRICING_ERROR: ${{ needs.agent.outputs.missing_model_pricing_error }} | |
| GH_AW_MISSING_MODEL_PRICING_MODEL_NAME: ${{ needs.agent.outputs.missing_model_pricing_model_name }} | |
| GH_AW_SHELL_EXPANSION_GUARD_REJECTED: ${{ needs.agent.outputs.shell_expansion_guard_rejected }} | |
| 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_OAUTH_TOKEN_CHECK_FAILED: ${{ needs.activation.outputs.oauth_token_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: "false" | |
| GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true" | |
| GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true" | |
| GH_AW_TIMEOUT_MINUTES: "${{ fromJSON(vars.GH_AW_DEFAULT_TIMEOUT_MINUTES || '20') }}" | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, 'handle_agent_failure.cjs')); | |
| await main(); | |
| detection: | |
| needs: | |
| - activation | |
| - agent | |
| if: always() && needs.agent.result != 'skipped' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| copilot-requests: write | |
| timeout-minutes: 10 | |
| env: | |
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | |
| 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@bc8c008a419c5b7a29df6f5641edd35fd1c6ea85 # v0.87.10 | |
| 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: "AI Issue Triage" | |
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-triage.lock.yml@${{ github.ref }} | |
| GH_AW_INFO_VERSION: "1.0.80" | |
| GH_AW_INFO_AWF_VERSION: "v0.28.10" | |
| GH_AW_INFO_ENGINE_ID: "copilot" | |
| - name: Download activation artifact | |
| continue-on-error: true | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: activation | |
| path: /tmp/gh-aw | |
| - name: Download agent output artifact | |
| id: download-agent-output | |
| continue-on-error: true | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| pattern: "{agent,agent-output-fallback}" | |
| merge-multiple: true | |
| 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 | |
| if [ -f "/tmp/gh-aw/agent_output.json" ]; then | |
| echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Checkout repository for patch context | |
| if: needs.agent.outputs.has_patch == 'true' | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| 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.28.10@sha256:c01e6d16d11ea4f2a46cc023a9f402224a3b3861b026818eec0dc586d7e6918e ghcr.io/github/gh-aw-firewall/api-proxy:0.28.10@sha256:c3a18aebb8251339117ea998296315de17bada366f8d03919b3348ea71112e64 ghcr.io/github/gh-aw-firewall/squid:0.28.10@sha256:c06076f7aca95df713e0748c44d80c0a3c2538fad67bfdd04296d45158e083e6 | |
| - 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: | | |
| bash "${RUNNER_TEMP}/gh-aw/actions/prepare_threat_detection_files.sh" | |
| - name: Setup threat detection | |
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| WORKFLOW_NAME: "AI Issue Triage" | |
| WORKFLOW_DESCRIPTION: "Maintain one concise issue summary with likely duplicates and missing-information guidance." | |
| HAS_PATCH: ${{ needs.agent.outputs.has_patch }} | |
| GH_AW_DETECTION_CONTINUE_ON_ERROR: "true" | |
| GH_AW_DETECTION_SKIP_PROMPT_SUMMARY: "true" | |
| with: | |
| script: | | |
| const path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, '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: Install AWF binary | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.28.10 --rootless | |
| - name: Install GitHub Copilot CLI | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" | |
| env: | |
| GH_HOST: github.com | |
| GH_AW_COMPILED_VERSION: v0.87.10 | |
| - name: Install threat-detect binary | |
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | |
| continue-on-error: true | |
| run: | | |
| bash "${RUNNER_TEMP}/gh-aw/actions/install_threat_detect_binary.sh" v0.5.1 | |
| - name: Execute threat detection with AWF | |
| id: detection_agentic_execution | |
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | |
| continue-on-error: true | |
| timeout-minutes: 10 | |
| env: | |
| AWF_REFLECT_ENABLED: 1 | |
| COPILOT_AGENT_RUNNER_TYPE: STANDALONE | |
| COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode | |
| COPILOT_GITHUB_TOKEN: ${{ github.token }} | |
| COPILOT_MODEL: small | |
| GH_AW_HARNESS_MAX_RETRIES: 0 | |
| GH_AW_LLM_PROVIDER: github | |
| 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: 10 | |
| GH_AW_VERSION: v0.87.10 | |
| 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 }} | |
| S2STOKENS: true | |
| 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) || '' }} | |
| WORKFLOW_NAME: "AI Issue Triage" | |
| WORKFLOW_DESCRIPTION: "Maintain one concise issue summary with likely duplicates and missing-information guidance." | |
| HAS_PATCH: ${{ needs.agent.outputs.has_patch }} | |
| GH_AW_DETECTION_CONTINUE_ON_ERROR: "true" | |
| run: | | |
| set -o pipefail | |
| printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt | |
| GH_AW_COPILOT_SRC="$(command -v copilot 2>/dev/null || true)" | |
| if [ -z "$GH_AW_COPILOT_SRC" ] || [ ! -x "$GH_AW_COPILOT_SRC" ]; then | |
| echo "GitHub Copilot CLI executable not found on PATH after installation" >&2 | |
| exit 127 | |
| fi | |
| GH_AW_COPILOT_BIN="${RUNNER_TEMP}/gh-aw/bin/copilot" | |
| mkdir -p "${RUNNER_TEMP}/gh-aw/bin" | |
| if [ "$GH_AW_COPILOT_SRC" != "$GH_AW_COPILOT_BIN" ]; then | |
| cp "$GH_AW_COPILOT_SRC" "$GH_AW_COPILOT_BIN" | |
| fi | |
| chmod 755 "$GH_AW_COPILOT_BIN" | |
| (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log) | |
| GH_AW_MAX_AI_CREDITS="${{ vars.GH_AW_DEFAULT_DETECTION_MAX_AI_CREDITS || '400' }}" | |
| printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.28.10/awf-config.schema.json\",\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"maxCacheMisses\":5,\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.4\",\"gpt-5.5\",\"gpt-5.6\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"auto\":[\"copilot/auto\",\"large\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\",\"kimi\"],\"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*\"],\"detection\":[\"small\"],\"evals\":[\"small\"],\"fable\":[\"copilot/*fable*\",\"anthropic/*fable*\"],\"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-3.6-flash\":[\"copilot/gemini-3.6*flash*\",\"google/gemini-3.6*flash*\",\"gemini/gemini-3.6*flash*\"],\"gemini-3.7-flash\":[\"copilot/gemini-3.7*flash*\",\"google/gemini-3.7*flash*\",\"gemini/gemini-3.7*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-omni\":[\"copilot/gemini-omni*\",\"google/gemini-omni*\",\"gemini/gemini-omni*\"],\"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*\"],\"gpt-5.6\":[\"copilot/gpt-5.6*\",\"openai/gpt-5.6*\"],\"grok\":[\"copilot/*grok*\",\"openai/*grok*\"],\"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*\"],\"kimi\":[\"copilot/kimi*\",\"openai/kimi*\"],\"kiwi\":[\"copilot/kiwi*\",\"openai/kiwi*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"lyria\":[\"google/lyria*\",\"gemini/lyria*\",\"copilot/lyria*\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mai-code-1-flash-picker\":[\"copilot/MAI-Code-1-Flash-picker*\",\"copilot/mai-code-1-flash-picker*\",\"openai/MAI-Code-1-Flash-picker*\"],\"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\"],\"raptor-mini\":[\"copilot/raptor*\",\"openai/raptor*\"],\"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-5*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\",\"anthropic/*sonnet-5*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"veo\":[\"google/veo*\",\"gemini/veo*\"],\"vision\":[\"copilot/gemini-*image*\",\"google/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.28.10,squid=sha256:c06076f7aca95df713e0748c44d80c0a3c2538fad67bfdd04296d45158e083e6,agent=sha256:c01e6d16d11ea4f2a46cc023a9f402224a3b3861b026818eec0dc586d7e6918e,api-proxy=sha256:c3a18aebb8251339117ea998296315de17bada366f8d03919b3348ea71112e64,cli-proxy=sha256:a61070cb7f21840c5f2ec74d55b49adf0652d0348ce059015aaaca33a8cb6b45\"},\"logging\":{\"proxyLogsDir\":\"/tmp/gh-aw/sandbox/firewall/logs\",\"auditDir\":\"/tmp/gh-aw/sandbox/firewall/audit\"}}" > "${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 | |
| if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then | |
| _GH_AW_CHROOT_JSON=$(jq -c --arg src "${RUNNER_TEMP}/gh-aw" --arg user "$(id -un)" --argjson uid "$(id -u)" --argjson gid "$(id -g)" --arg home "${RUNNER_TEMP}/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" | |
| 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,SC2016,SC2086 | |
| 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"} --env-all --exclude-env ACTIONS_ID_TOKEN_REQUEST_TOKEN --exclude-env ACTIONS_ID_TOKEN_REQUEST_URL --exclude-env COPILOT_GITHUB_TOKEN --mount /tmp/gh-aw:/tmp/gh-aw:rw --mount /tmp/gh-aw/threat-detection:/tmp/gh-aw/threat-detection:rw --log-level info --skip-pull \ | |
| -- /bin/bash -c 'set +o histexpand; export PATH="${RUNNER_TEMP}/gh-aw/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; [ -n "$ERLANG_HOME" ] && export PATH="$ERLANG_HOME/bin:$PATH" || true && threat-detect --engine copilot --output /tmp/gh-aw/threat-detection/detection_result.json /tmp/gh-aw/threat-detection' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log | |
| - name: Render detection log | |
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | |
| continue-on-error: true | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| with: | |
| script: | | |
| const path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, 'render_detection_log.cjs')); | |
| await main(); | |
| - name: Copy detection firewall logs | |
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | |
| continue-on-error: true | |
| run: | | |
| mkdir -p /tmp/gh-aw/threat-detection/sandbox/firewall | |
| if [ -d /tmp/gh-aw/sandbox/firewall/logs ]; then mkdir -p /tmp/gh-aw/threat-detection/sandbox/firewall/logs && cp -r /tmp/gh-aw/sandbox/firewall/logs/. /tmp/gh-aw/threat-detection/sandbox/firewall/logs/; fi | |
| if [ -d /tmp/gh-aw/sandbox/firewall/audit ]; then mkdir -p /tmp/gh-aw/threat-detection/sandbox/firewall/audit && cp -r /tmp/gh-aw/sandbox/firewall/audit/. /tmp/gh-aw/threat-detection/sandbox/firewall/audit/; fi | |
| - name: Upload threat detection artifact | |
| if: always() && steps.detection_guard.outputs.run_detection == 'true' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: detection | |
| path: | | |
| /tmp/gh-aw/threat-detection/detection_result.json | |
| /tmp/gh-aw/threat-detection/sandbox/firewall/logs/ | |
| /tmp/gh-aw/threat-detection/sandbox/firewall/audit/ | |
| if-no-files-found: ignore | |
| - 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 path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, 'parse_token_usage.cjs')); | |
| await main(); | |
| - name: Conclude threat detection | |
| id: detection_conclusion | |
| if: always() | |
| continue-on-error: true | |
| 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" | |
| run: | | |
| bash "${RUNNER_TEMP}/gh-aw/actions/conclude_threat_detection.sh" /tmp/gh-aw/threat-detection/detection_result.json | |
| pre_activation: | |
| runs-on: ubuntu-slim | |
| permissions: | |
| actions: read | |
| env: | |
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | |
| outputs: | |
| activated: ${{ steps.check_rate_limit.outputs.rate_limit_ok == '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@bc8c008a419c5b7a29df6f5641edd35fd1c6ea85 # v0.87.10 | |
| with: | |
| destination: ${{ runner.temp }}/gh-aw/actions | |
| job-name: ${{ github.job }} | |
| env: | |
| GH_AW_SETUP_WORKFLOW_NAME: "AI Issue Triage" | |
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-triage.lock.yml@${{ github.ref }} | |
| GH_AW_INFO_VERSION: "1.0.80" | |
| GH_AW_INFO_AWF_VERSION: "v0.28.10" | |
| GH_AW_INFO_ENGINE_ID: "copilot" | |
| - name: Check user rate limit | |
| id: check_rate_limit | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_RATE_LIMIT_MAX: "5" | |
| GH_AW_RATE_LIMIT_WINDOW: "60" | |
| GH_AW_RATE_LIMIT_EVENTS: "issues" | |
| GH_AW_RATE_LIMIT_IGNORED_ROLES: "admin,maintain,write" | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, 'check_rate_limit.cjs')); | |
| await main(); | |
| publish_triage_summary: | |
| needs: | |
| - agent | |
| - detection | |
| if: > | |
| (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'publish_triage_summary') && | |
| (needs.detection.result == 'success' && needs.detection.outputs.detection_success == 'true') | |
| runs-on: ubuntu-slim | |
| permissions: | |
| contents: read | |
| issues: write | |
| steps: | |
| - name: Download agent output artifact | |
| continue-on-error: true | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| pattern: "{agent,agent-output-fallback}" | |
| merge-multiple: true | |
| path: ${{ runner.temp }}/gh-aw/safe-jobs/ | |
| - name: Check out trusted workflow source | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| env: | |
| GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json | |
| with: | |
| persist-credentials: false | |
| ref: ${{ github.sha }} | |
| - name: Set up Python | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| env: | |
| GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json | |
| with: | |
| python-version: "3.12" | |
| - name: Rebuild current deterministic evidence | |
| id: refresh | |
| run: python .github/scripts/issue-triage/issue-context.py "$GITHUB_EVENT_PATH" "$RUNNER_TEMP/verified-issue-context.md" "$RUNNER_TEMP/verified-triage-event.json" "$RUNNER_TEMP/verified-evidence.json" | |
| env: | |
| GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json | |
| GITHUB_TOKEN: ${{ github.token }} | |
| ISSUE_TRIAGE_FORCE_EVIDENCE: "true" | |
| - name: Rebuild sanitized bug report context | |
| if: steps.refresh.outputs.should_process == 'true' | |
| run: python .github/scripts/issue-triage/bug-report-analyzer.py "$RUNNER_TEMP/verified-triage-event.json" "$RUNNER_TEMP/verified-bug-report-context.md" | |
| env: | |
| GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json | |
| - name: Verify agent output against current evidence | |
| if: steps.refresh.outputs.should_process == 'true' | |
| run: python .github/scripts/issue-triage/verify-agent-output.py "$GH_AW_AGENT_OUTPUT" "$RUNNER_TEMP/verified-evidence.json" "$RUNNER_TEMP/verified-bug-report-context.md" "$RUNNER_TEMP/verified-triage-output.json" | |
| env: | |
| GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json | |
| - name: Upsert canonical triage summary | |
| if: steps.refresh.outputs.should_process == 'true' | |
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | |
| env: | |
| GH_AW_AGENT_OUTPUT: ${{ runner.temp }}/gh-aw/safe-jobs/agent_output.json | |
| ISSUE_TRIAGE_VERIFIED_OUTPUT: ${{ runner.temp }}/verified-triage-output.json | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const marker = '<!-- powertoys-ai-triage:canonical:v1 -->'; | |
| const outputPath = process.env.GH_AW_AGENT_OUTPUT; | |
| if (!outputPath || !fs.existsSync(outputPath)) { | |
| core.setFailed('Agent output is unavailable'); | |
| return; | |
| } | |
| const output = JSON.parse(fs.readFileSync(outputPath, 'utf8')); | |
| const item = output.items?.find( | |
| candidate => candidate.type === 'publish_triage_summary' | |
| ); | |
| if (!item) { | |
| core.setFailed('The agent did not provide a triage summary'); | |
| return; | |
| } | |
| const verifiedPath = process.env.ISSUE_TRIAGE_VERIFIED_OUTPUT; | |
| if (!verifiedPath || !fs.existsSync(verifiedPath)) { | |
| core.setFailed('Verified triage output is unavailable'); | |
| return; | |
| } | |
| const verified = JSON.parse(fs.readFileSync(verifiedPath, 'utf8')); | |
| const bounded = (value, max, fallback) => { | |
| if (typeof value !== 'string') return fallback; | |
| const normalized = value.replace(/\0/g, '').trim(); | |
| return normalized ? normalized.slice(0, max) : fallback; | |
| }; | |
| const escapeMarkdown = value => String(value) | |
| .replaceAll('\\', '\\\\') | |
| .replaceAll('&', '&') | |
| .replaceAll('<', '<') | |
| .replaceAll('>', '>') | |
| .replaceAll('@', '@\u200B') | |
| .replace(/([`*_{}\[\]()#+.!|~-])/g, '\\$1') | |
| .replace(/\r?\n+/g, ' ') | |
| .trim(); | |
| const formatTechnicalMarkdown = value => { | |
| const tokens = []; | |
| const withPlaceholders = String(value) | |
| .replace(/\0/g, '') | |
| .replace( | |
| /\b(?:0x[0-9a-f]{6,}|[\w.-]+\.(?:xaml\.cs|dll|exe|json|log|xaml|cs))\b/gi, | |
| token => { | |
| const placeholder = `GHCODETOKEN${tokens.length}GH`; | |
| tokens.push(token); | |
| return placeholder; | |
| } | |
| ); | |
| let formatted = escapeMarkdown(withPlaceholders); | |
| tokens.forEach((token, index) => { | |
| formatted = formatted.replace( | |
| `GHCODETOKEN${index}GH`, | |
| `\`${token.replaceAll('`', '')}\`` | |
| ); | |
| }); | |
| return formatted; | |
| }; | |
| const redactDiagnostic = value => String(value) | |
| .replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, '<email>') | |
| .replace(/\b(?:25[0-5]|2[0-4]\d|1?\d?\d)(?:\.(?:25[0-5]|2[0-4]\d|1?\d?\d)){3}\b/g, '<ip-address>') | |
| .replace(/[A-Z]:\\Users\\[^\\\s"']+/gi, '<user-profile>') | |
| .replace(/\/(?:home|Users)\/[^/\s"']+/gi, '/<user>') | |
| .replace(/\b[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b/gi, '<guid>') | |
| .replace(/\bS-1-5-(?:\d+-){1,14}\d+\b/g, '<sid>') | |
| .replace(/\bhttps?:\/\/[^\s<>"]+/gi, '<url>') | |
| .replace(/\b(token|secret|password|securitykey)\b\s*[:=]\s*[^\s,;]+/gi, '$1=<redacted>') | |
| .replace(/\b(?:machine|computer|user)(?:name)?\b\s*[:=]\s*[^\s,;]+/gi, '<identity>=<redacted>'); | |
| const summary = bounded(item.summary, 800, 'The issue needs maintainer review.'); | |
| const inputSha256 = verified.input_sha256; | |
| const area = verified.suggested_area; | |
| const requestedProductLabel = verified.product_label; | |
| const powertoysVersion = verified.powertoys_version; | |
| const reportedVersion = | |
| powertoysVersion === 'Not provided' ? null : powertoysVersion; | |
| const numericVersion = value => { | |
| const match = String(value || '').match( | |
| /\b(?:v)?(\d+(?:\.\d+){1,3})(?:-[A-Za-z0-9.-]+)?\b/ | |
| ); | |
| return match | |
| ? match[1].split('.').map(part => Number(part)) | |
| : null; | |
| }; | |
| const compareVersions = (left, right) => { | |
| const leftParts = numericVersion(left); | |
| const rightParts = numericVersion(right); | |
| if (!leftParts || !rightParts) return null; | |
| const width = Math.max(leftParts.length, rightParts.length); | |
| for (let index = 0; index < width; index += 1) { | |
| const difference = | |
| (leftParts[index] || 0) - (rightParts[index] || 0); | |
| if (difference !== 0) return Math.sign(difference); | |
| } | |
| return 0; | |
| }; | |
| let latestStableVersion = null; | |
| let latestStableUrl = null; | |
| try { | |
| const latestRelease = await github.request( | |
| 'GET /repos/{owner}/{repo}/releases/latest', | |
| { owner: 'microsoft', repo: 'PowerToys' } | |
| ); | |
| if (!latestRelease.data?.prerelease) { | |
| latestStableVersion = String( | |
| latestRelease.data?.tag_name || '' | |
| ).match( | |
| /\b(?:v)?(\d+(?:\.\d+){1,3}(?:-[A-Za-z0-9.-]+)?)\b/ | |
| )?.[1] || null; | |
| latestStableUrl = /^https:\/\/github\.com\/microsoft\/PowerToys\/releases\/tag\/[^/\s]+$/.test( | |
| String(latestRelease.data?.html_url || '') | |
| ) | |
| ? latestRelease.data.html_url | |
| : null; | |
| } | |
| } catch (error) { | |
| core.warning( | |
| `Latest stable PowerToys release could not be checked: ${error.message}` | |
| ); | |
| } | |
| const isOutdated = | |
| reportedVersion && | |
| latestStableVersion && | |
| compareVersions(reportedVersion, latestStableVersion) === -1; | |
| let hasMissingInformation = | |
| item.has_missing_information === true || | |
| item.has_missing_information === 'true'; | |
| let missingInformation = bounded( | |
| item.missing_information, | |
| 800, | |
| hasMissingInformation ? 'Additional investigation details are needed.' : 'None' | |
| ); | |
| const issueKind = verified.issue_kind; | |
| if ( | |
| issueKind === 'OTHER' && | |
| /\b(?:bug report|report ZIP|ZIP file)\b/i.test(missingInformation) | |
| ) { | |
| hasMissingInformation = false; | |
| missingInformation = 'None'; | |
| } | |
| const reproductionQuality = verified.reproduction_quality; | |
| const bugReportRequirement = verified.bug_report_requirement; | |
| const bugReportStatus = verified.bug_report_status; | |
| const requestedBugReportConfidence = String( | |
| item.bug_report_confidence || '' | |
| ).toUpperCase(); | |
| const bugReportConfidence = ['HIGH', 'MEDIUM', 'LOW', 'NONE'].includes( | |
| requestedBugReportConfidence | |
| ) ? requestedBugReportConfidence : 'NONE'; | |
| const requestedIssueLanguage = String( | |
| item.issue_language || '' | |
| ).toUpperCase(); | |
| const issueLanguage = [ | |
| 'ENGLISH', 'NON_ENGLISH', 'UNCERTAIN' | |
| ].includes(requestedIssueLanguage) | |
| ? requestedIssueLanguage | |
| : 'UNCERTAIN'; | |
| const needsEnglishTranslation = issueLanguage === 'NON_ENGLISH'; | |
| const bugReportFindings = bounded( | |
| redactDiagnostic( | |
| typeof item.bug_report_findings === 'string' | |
| ? item.bug_report_findings | |
| : '' | |
| ), | |
| 1200, | |
| 'No diagnostic findings were provided.' | |
| ); | |
| if (issueKind === 'BUG') { | |
| const missingReproduction = | |
| !needsEnglishTranslation && | |
| reproductionQuality !== 'SUFFICIENT'; | |
| const missingReport = | |
| bugReportRequirement === 'REQUIRED' && | |
| bugReportStatus !== 'ANALYZED'; | |
| hasMissingInformation = missingReproduction || missingReport; | |
| if (!hasMissingInformation) { | |
| missingInformation = 'None'; | |
| } else if (missingReproduction && missingReport) { | |
| missingInformation = bugReportStatus === 'REJECTED' | |
| ? 'Please provide concrete steps to reproduce and attach a newly generated PowerToys bug report ZIP.' | |
| : 'Please provide concrete steps to reproduce and attach the PowerToys bug report ZIP.'; | |
| } else if (missingReproduction) { | |
| missingInformation = 'Please provide concrete steps to reproduce the issue.'; | |
| } else { | |
| missingInformation = bugReportStatus === 'REJECTED' | |
| ? 'Please attach a newly generated PowerToys bug report ZIP.' | |
| : 'Please attach the PowerToys bug report ZIP.'; | |
| } | |
| } | |
| let requestedDuplicates; | |
| try { | |
| requestedDuplicates = JSON.parse(item.duplicate_candidates_json); | |
| } catch { | |
| core.setFailed('duplicate_candidates_json is not valid JSON'); | |
| return; | |
| } | |
| if (!Array.isArray(requestedDuplicates) || requestedDuplicates.length > 5) { | |
| core.setFailed('duplicate_candidates_json must be an array with at most five items'); | |
| return; | |
| } | |
| const issueNumber = context.issue.number; | |
| const getCurrentIssue = async () => { | |
| const response = await github.rest.issues.get({ | |
| ...context.repo, | |
| issue_number: issueNumber | |
| }); | |
| return response.data; | |
| }; | |
| const skipWriteIfClosed = async action => { | |
| const currentIssue = await getCurrentIssue(); | |
| if (currentIssue.state === 'open') return false; | |
| core.notice(`Issue is closed; ${action} was skipped.`); | |
| return true; | |
| }; | |
| const allowedDuplicateNumbers = new Set( | |
| verified.requested_duplicate_numbers | |
| ); | |
| const verifiedDuplicates = []; | |
| const seen = new Set(); | |
| for (const candidate of requestedDuplicates) { | |
| const number = Number(candidate?.number); | |
| if (!Number.isSafeInteger(number) || number <= 0 || | |
| number === issueNumber || seen.has(number) || | |
| !allowedDuplicateNumbers.has(number)) { | |
| continue; | |
| } | |
| seen.add(number); | |
| try { | |
| const response = await github.rest.issues.get({ | |
| ...context.repo, | |
| issue_number: number | |
| }); | |
| if (response.data.pull_request) continue; | |
| verifiedDuplicates.push({ | |
| number, | |
| id: response.data.id, | |
| title: bounded(response.data.title, 300, `Issue ${number}`), | |
| reason: bounded(candidate?.reason, 300, 'Describes the same underlying report.'), | |
| confidence: ['HIGH', 'MEDIUM', 'LOW'].includes( | |
| String(candidate?.confidence || '').toUpperCase() | |
| ) ? String(candidate.confidence).toUpperCase() : 'MEDIUM' | |
| }); | |
| } catch (error) { | |
| if (error.status !== 404) throw error; | |
| } | |
| } | |
| const confidenceRank = { HIGH: 3, MEDIUM: 2, LOW: 1 }; | |
| verifiedDuplicates.sort( | |
| (left, right) => | |
| confidenceRank[right.confidence] - confidenceRank[left.confidence] || | |
| left.number - right.number | |
| ); | |
| const duplicateSection = verifiedDuplicates.length | |
| ? verifiedDuplicates.map(candidate => | |
| [ | |
| '<details>', | |
| `<summary>#${candidate.number} — ${formatTechnicalMarkdown(candidate.title)}</summary>`, | |
| '', | |
| `**Why this may be a duplicate:** ${formatTechnicalMarkdown(candidate.reason)}`, | |
| '</details>' | |
| ].join('\n') | |
| ).join('\n\n') | |
| : ''; | |
| const author = verified.issue_author; | |
| const authorActions = []; | |
| if (hasMissingInformation) { | |
| authorActions.push( | |
| `**Needed:** ${formatTechnicalMarkdown(missingInformation)}` | |
| ); | |
| } | |
| if (needsEnglishTranslation) { | |
| authorActions.push( | |
| '**Needed:** Please translate the issue title and description to English.' | |
| ); | |
| } | |
| const updateRecommendation = | |
| isOutdated | |
| ? [ | |
| '**Recommended:** Please update PowerToys', | |
| `from \`${reportedVersion.replaceAll('`', '')}\``, | |
| latestStableUrl | |
| ? `to the latest stable release, [\`${latestStableVersion.replaceAll('`', '')}\`](${latestStableUrl}),` | |
| : `to the latest stable release, \`${latestStableVersion.replaceAll('`', '')}\`,`, | |
| 'and confirm whether the issue still reproduces.' | |
| ].join(' ') | |
| : ''; | |
| if (updateRecommendation) { | |
| authorActions.push(updateRecommendation); | |
| } | |
| const authorSection = authorActions.length | |
| ? [ | |
| author | |
| ? `@${author}, please review the following:` | |
| : 'Issue author, please review the following:', | |
| '', | |
| ...authorActions.map(action => `- ${action}`) | |
| ] | |
| : []; | |
| const repositoryLabels = await github.paginate( | |
| github.rest.issues.listLabelsForRepo, | |
| { ...context.repo, per_page: 100 } | |
| ); | |
| const productLabels = repositoryLabels | |
| .map(label => label.name) | |
| .filter(name => name.startsWith('Product-')); | |
| const normalizeLabel = value => String(value) | |
| .toLowerCase() | |
| .replace(/[^a-z0-9]+/g, ''); | |
| const desiredProductLabel = | |
| productLabels.find( | |
| label => | |
| normalizeLabel(label.slice('Product-'.length)) === | |
| normalizeLabel(area) | |
| ) || | |
| productLabels.find( | |
| label => label.toLowerCase() === requestedProductLabel.toLowerCase() | |
| ) || | |
| null; | |
| const bodyLines = [ | |
| marker, | |
| `<!-- powertoys-ai-triage:input-sha256:${inputSha256} -->`, | |
| '## 🧭 Triage summary', | |
| '' | |
| ]; | |
| if (authorSection.length) { | |
| bodyLines.push( | |
| '### 🙋 For the issue author', | |
| '', | |
| ...authorSection, | |
| '' | |
| ); | |
| } | |
| bodyLines.push( | |
| '### 🛠️ For the PowerToys team', | |
| '', | |
| [ | |
| desiredProductLabel | |
| ? `**🧩 ${escapeMarkdown(desiredProductLabel.slice('Product-'.length))}**` | |
| : `**🧩 ${escapeMarkdown(area === 'Unknown' ? 'Unclassified' : area)}**`, | |
| issueKind === 'BUG' ? '**🐞 Bug**' : '**📌 Issue**', | |
| powertoysVersion !== 'Not provided' | |
| ? `**📦 PowerToys \`${powertoysVersion.replaceAll('`', '')}\`**` | |
| : null | |
| ].filter(Boolean).join(' · '), | |
| '', | |
| formatTechnicalMarkdown(summary), | |
| '' | |
| ); | |
| if (issueKind === 'BUG' && bugReportStatus === 'ANALYZED') { | |
| bodyLines.push( | |
| '### 🔎 Diagnostic finding', | |
| '', | |
| `${formatTechnicalMarkdown(bugReportFindings)} _(${bugReportConfidence.toLowerCase()} confidence)_`, | |
| '' | |
| ); | |
| } else if (issueKind === 'BUG' && bugReportStatus === 'REJECTED') { | |
| bodyLines.push( | |
| '### ⚠️ Bug report', | |
| '', | |
| `The attached report could not be safely analyzed: ${formatTechnicalMarkdown(bugReportFindings)}`, | |
| '' | |
| ); | |
| } | |
| if (verifiedDuplicates.length) { | |
| bodyLines.push('### 🔁 Possible duplicates', '', duplicateSection, ''); | |
| } | |
| if (issueKind === 'BUG') { | |
| const reportDetail = bugReportStatus === 'ANALYZED' | |
| ? '✅ Analyzed from a sanitized diagnostic subset; the raw archive was discarded.' | |
| : bugReportRequirement === 'REQUIRED' | |
| ? `⚠️ Required for this failure type; ${bugReportStatus.replaceAll('_', ' ').toLowerCase()}` | |
| : bugReportRequirement === 'OPTIONAL' | |
| ? 'ℹ️ Not attached; optional for this clear UI/visual report.' | |
| : 'ℹ️ Not attached; may help later but does not block triage.'; | |
| const reproductionDetail = reproductionQuality === 'SUFFICIENT' | |
| ? '✅ Sufficient' | |
| : needsEnglishTranslation | |
| ? '⏳ Reassess after English translation' | |
| : reproductionQuality === 'INSUFFICIENT' | |
| ? '⚠️ Needs more detail' | |
| : 'Not applicable'; | |
| bodyLines.push( | |
| '<details>', | |
| '<summary>🧪 Investigation details</summary>', | |
| '', | |
| `- **Reproduction:** ${reproductionDetail}`, | |
| `- **Bug report:** ${reportDetail}`, | |
| '</details>', | |
| '' | |
| ); | |
| } | |
| bodyLines.push( | |
| '_AI-assisted automated triage; PowerToys maintainers make final decisions._', | |
| '', | |
| '<!-- gh-aw-workflow-id: issue-triage -->' | |
| ); | |
| const body = bodyLines.join('\n'); | |
| if (await skipWriteIfClosed('triage publication')) return; | |
| const comments = await github.paginate(github.rest.issues.listComments, { | |
| ...context.repo, | |
| issue_number: issueNumber, | |
| per_page: 100 | |
| }); | |
| const canonical = comments | |
| .filter(comment => | |
| comment.user?.login === 'github-actions[bot]' && | |
| typeof comment.body === 'string' && | |
| comment.body.includes(marker) | |
| ) | |
| .sort((left, right) => left.id - right.id)[0]; | |
| if (await skipWriteIfClosed('triage publication')) return; | |
| let comment; | |
| if (canonical) { | |
| comment = await github.rest.issues.updateComment({ | |
| ...context.repo, | |
| comment_id: canonical.id, | |
| body | |
| }); | |
| } else { | |
| comment = await github.rest.issues.createComment({ | |
| ...context.repo, | |
| issue_number: issueNumber, | |
| body | |
| }); | |
| } | |
| if (comment.data.pin != null) { | |
| await github.request( | |
| 'DELETE /repos/{owner}/{repo}/issues/comments/{comment_id}/pin', | |
| { ...context.repo, comment_id: comment.data.id } | |
| ); | |
| } | |
| if (await skipWriteIfClosed('triage label updates')) return; | |
| const needsAuthorFeedback = | |
| needsEnglishTranslation || hasMissingInformation; | |
| const currentLabels = new Set( | |
| verified.current_labels | |
| ); | |
| if (needsAuthorFeedback && !currentLabels.has('Needs-Author-Feedback')) { | |
| await github.rest.issues.addLabels({ | |
| ...context.repo, | |
| issue_number: issueNumber, | |
| labels: ['Needs-Author-Feedback'] | |
| }); | |
| } else if (!needsAuthorFeedback && currentLabels.has('Needs-Author-Feedback')) { | |
| try { | |
| await github.rest.issues.removeLabel({ | |
| ...context.repo, | |
| issue_number: issueNumber, | |
| name: 'Needs-Author-Feedback' | |
| }); | |
| } catch (error) { | |
| if (error.status !== 404) throw error; | |
| } | |
| } | |
| if (desiredProductLabel && !currentLabels.has(desiredProductLabel)) { | |
| await github.rest.issues.addLabels({ | |
| ...context.repo, | |
| issue_number: issueNumber, | |
| labels: [desiredProductLabel] | |
| }); | |
| } | |
| if (verifiedDuplicates.length) { | |
| if (await skipWriteIfClosed('the duplicate suggestion')) return; | |
| const strongest = verifiedDuplicates[0]; | |
| const suggestionStartedAt = Math.floor(Date.now() / 1000); | |
| const response = await github.request( | |
| 'PATCH /repos/{owner}/{repo}/issues/{issue_number}', | |
| { | |
| ...context.repo, | |
| issue_number: issueNumber, | |
| state: { | |
| value: 'closed', | |
| rationale: | |
| `${strongest.reason} Suggested canonical issue: #${strongest.number}.`, | |
| confidence: strongest.confidence, | |
| suggest: true | |
| }, | |
| state_reason: 'duplicate', | |
| duplicate_issue_id: strongest.id, | |
| headers: { | |
| accept: 'application/vnd.github+json', | |
| 'X-GitHub-Api-Version': '2026-03-10' | |
| } | |
| } | |
| ); | |
| if (response.data?.state === 'closed') { | |
| const currentIssue = await getCurrentIssue(); | |
| const closedAt = Date.parse(currentIssue.closed_at); | |
| const closedBySuggestion = | |
| currentIssue.state === 'closed' && | |
| currentIssue.closed_by?.login === 'github-actions[bot]' && | |
| Number.isFinite(closedAt) && | |
| Math.floor(closedAt / 1000) >= suggestionStartedAt; | |
| if (closedBySuggestion) { | |
| await github.rest.issues.update({ | |
| ...context.repo, | |
| issue_number: issueNumber, | |
| state: 'open' | |
| }); | |
| core.setFailed( | |
| 'GitHub applied the close instead of holding it for review; the issue was reopened' | |
| ); | |
| } else { | |
| core.setFailed( | |
| 'GitHub returned the issue as closed after the duplicate suggestion, but the workflow did not reopen it because the closure was not caused by this request' | |
| ); | |
| } | |
| } | |
| } | |
| safe_outputs: | |
| needs: | |
| - activation | |
| - agent | |
| - detection | |
| if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success' | |
| runs-on: ubuntu-slim | |
| permissions: {} | |
| 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 }}/issue-triage" | |
| 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: "small" | |
| GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }} | |
| GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }} | |
| GH_AW_WORKFLOW_EMOJI: "📌" | |
| GH_AW_WORKFLOW_ID: "issue-triage" | |
| GH_AW_WORKFLOW_NAME: "AI Issue Triage" | |
| GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/issue-triage.md" | |
| outputs: | |
| 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 }} | |
| 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_items_applied: ${{ steps.process_safe_outputs.outputs.items_applied }} | |
| process_safe_outputs_items_cancelled: ${{ steps.process_safe_outputs.outputs.items_cancelled }} | |
| process_safe_outputs_items_deferred: ${{ steps.process_safe_outputs.outputs.items_deferred }} | |
| process_safe_outputs_items_failed: ${{ steps.process_safe_outputs.outputs.items_failed }} | |
| process_safe_outputs_items_skipped: ${{ steps.process_safe_outputs.outputs.items_skipped }} | |
| process_safe_outputs_items_succeeded: ${{ steps.process_safe_outputs.outputs.items_succeeded }} | |
| process_safe_outputs_items_warnings: ${{ steps.process_safe_outputs.outputs.items_warnings }} | |
| process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} | |
| process_safe_outputs_status: ${{ steps.process_safe_outputs.outputs.status }} | |
| 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@bc8c008a419c5b7a29df6f5641edd35fd1c6ea85 # v0.87.10 | |
| 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: "AI Issue Triage" | |
| GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/issue-triage.lock.yml@${{ github.ref }} | |
| GH_AW_INFO_VERSION: "1.0.80" | |
| GH_AW_INFO_AWF_VERSION: "v0.28.10" | |
| GH_AW_INFO_ENGINE_ID: "copilot" | |
| - name: Mask OTLP telemetry headers | |
| run: bash "${RUNNER_TEMP}/gh-aw/actions/mask_otlp_headers.sh" | |
| - name: Download agent output artifact | |
| id: download-agent-output | |
| continue-on-error: true | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| pattern: "{agent,agent-output-fallback}" | |
| merge-multiple: true | |
| 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 | |
| if [ -f "/tmp/gh-aw/agent_output.json" ]; then | |
| echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT" | |
| fi | |
| - 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: "api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,s.symcb.com,s.symcd.com,security.ubuntu.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_OUTPUT_JOBS: "{\"publish_triage_summary\":\"\"}" | |
| GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}" | |
| with: | |
| github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} | |
| script: | | |
| const path = require('path'); | |
| const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions'); | |
| const { setupGlobals } = require(path.join(actionsDir, 'setup_globals.cjs')); | |
| setupGlobals(core, github, context, exec, io, getOctokit); | |
| const { main } = require(path.join(actionsDir, 'process_safe_outputs.cjs')); | |
| await main(); | |
| - name: Upload Safe Outputs Items | |
| if: always() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: safe-outputs-items | |
| path: | | |
| /tmp/gh-aw/safe-output-items.jsonl | |
| /tmp/gh-aw/temporary-id-map.json | |
| /tmp/gh-aw/safe-output-errors.json | |
| if-no-files-found: ignore |