Skip to content

Bug in safe-outputs Frontmatter Merging Schematics #25349

@chrisfregly

Description

@chrisfregly

Issue

Blocks of different types in safe-outputs i.e. noop, threat-detection, etc are not merged from imports. However per documentation:

safe-outputs: Each type defined once; main overrides imports. Duplicate types across imports fail.

Example

hello-world.md

---
description: hello world
on:
  workflow_dispatch:
imports:
  - abc.md
safe-outputs:
  noop:
    report-as-issue: false
---
Print "hello world!".

abc.md

---
safe-outputs:
  threat-detection:
    steps:
      - name: Print abc
        run: echo "abc"
---

Run

gh aw compile hello-world.md

Output

The Print abc threat detection step should be included but is not generated as a part of the lock file.

hello-world.lock
# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"d02527a15cd27ea243870af980436e030bfc95716e067ed1e5c7bee67490509c","compiler_version":"v0.67.1","strict":true,"agent_id":"copilot"}
#    ___                   _   _      
#   / _ \                 | | (_)     
#  | |_| | __ _  ___ _ __ | |_ _  ___ 
#  |  _  |/ _` |/ _ \ '_ \| __| |/ __|
#  | | | | (_| |  __/ | | | |_| | (__ 
#  \_| |_/\__, |\___|_| |_|\__|_|\___|
#          __/ |
#  _    _ |___/ 
# | |  | |                / _| |
# | |  | | ___ _ __ _  __| |_| | _____      ____
# | |/\| |/ _ \ '__| |/ /|  _| |/ _ \ \ /\ / / ___|
# \  /\  / (_) | | | | ( | | | | (_) \ V  V /\__ \
#  \/  \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
#
# This file was automatically generated by gh-aw (v0.67.1). DO NOT EDIT.
#
# 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/
#
# hello world
#
# Resolved workflow manifest:
#   Imports:
#     - abc.md
#
# Secrets used:
#   - COPILOT_GITHUB_TOKEN
#   - GH_AW_GITHUB_MCP_SERVER_TOKEN
#   - GH_AW_GITHUB_TOKEN
#   - GITHUB_TOKEN
#
# Custom actions used:
#   - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
#   - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
#   - actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
#   - actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
#   - github/gh-aw-actions/setup@80471a493be8c528dd27daf73cd644242a7965e0 # v0.67.1

name: "Hello World"
"on":
  workflow_dispatch:
    inputs:
      aw_context:
        default: ""
        description: Agent caller context (used internally by Agentic Workflows).
        required: false
        type: string

permissions: {}

concurrency:
  group: "gh-aw-${{ github.workflow }}"

run-name: "Hello World"

jobs:
  activation:
    runs-on: ubuntu-slim
    permissions:
      contents: read
    outputs:
      comment_id: ""
      comment_repo: ""
      lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }}
      model: ${{ steps.generate_aw_info.outputs.model }}
      secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
      setup-trace-id: ${{ steps.setup.outputs.trace-id }}
    steps:
      - name: Setup Scripts
        id: setup
        uses: github/gh-aw-actions/setup@80471a493be8c528dd27daf73cd644242a7965e0 # v0.67.1
        with:
          destination: ${{ runner.temp }}/gh-aw/actions
          job-name: ${{ github.job }}
      - name: Generate agentic run info
        id: generate_aw_info
        env:
          GH_AW_INFO_ENGINE_ID: "copilot"
          GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI"
          GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'auto' }}
          GH_AW_INFO_VERSION: "latest"
          GH_AW_INFO_AGENT_VERSION: "latest"
          GH_AW_INFO_CLI_VERSION: "v0.67.1"
          GH_AW_INFO_WORKFLOW_NAME: "Hello World"
          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.25.13"
          GH_AW_INFO_AWMG_VERSION: ""
          GH_AW_INFO_FIREWALL_TYPE: "squid"
          GH_AW_COMPILED_STRICT: "true"
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/generate_aw_info.cjs');
            await main(core, context);
      - name: Validate COPILOT_GITHUB_TOKEN secret
        id: validate-secret
        run: ${RUNNER_TEMP}/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
        env:
          COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
      - name: Checkout .github and .agents folders
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          persist-credentials: false
          sparse-checkout: |
            .github
            .agents
          sparse-checkout-cone-mode: true
          fetch-depth: 1
      - name: Check workflow lock file
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_WORKFLOW_FILE: "hello-world.lock.yml"
          GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}"
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/check_workflow_timestamp_api.cjs');
            await main();
      - name: Check compile-agentic version
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_COMPILED_VERSION: "v0.67.1"
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/check_version_updates.cjs');
            await main();
      - name: Create prompt with built-in context
        env:
          GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
          GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl
          GH_AW_GITHUB_ACTOR: ${{ github.actor }}
          GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }}
          GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }}
          GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
          GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }}
          GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
          GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
          GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
        # poutine:ignore untrusted_checkout_exec
        run: |
          bash ${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh
          {
          cat << 'GH_AW_PROMPT_93fb119cfe9fd910_EOF'
          <system>
          GH_AW_PROMPT_93fb119cfe9fd910_EOF
          cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md"
          cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md"
          cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md"
          cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md"
          cat << 'GH_AW_PROMPT_93fb119cfe9fd910_EOF'
          <safe-output-tools>
          Tools: create_issue, missing_tool, missing_data, noop
          GH_AW_PROMPT_93fb119cfe9fd910_EOF
          cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_auto_create_issue.md"
          cat << 'GH_AW_PROMPT_93fb119cfe9fd910_EOF'
          </safe-output-tools>
          <github-context>
          The following GitHub context information is available for this workflow:
          {{#if __GH_AW_GITHUB_ACTOR__ }}
          - **actor**: __GH_AW_GITHUB_ACTOR__
          {{/if}}
          {{#if __GH_AW_GITHUB_REPOSITORY__ }}
          - **repository**: __GH_AW_GITHUB_REPOSITORY__
          {{/if}}
          {{#if __GH_AW_GITHUB_WORKSPACE__ }}
          - **workspace**: __GH_AW_GITHUB_WORKSPACE__
          {{/if}}
          {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }}
          - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__
          {{/if}}
          {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }}
          - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__
          {{/if}}
          {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }}
          - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__
          {{/if}}
          {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }}
          - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__
          {{/if}}
          {{#if __GH_AW_GITHUB_RUN_ID__ }}
          - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__
          {{/if}}
          </github-context>
          
          GH_AW_PROMPT_93fb119cfe9fd910_EOF
          cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
          cat << 'GH_AW_PROMPT_93fb119cfe9fd910_EOF'
          </system>
          {{#runtime-import .github/workflows/abc.md}}
          {{#runtime-import .github/workflows/hello-world.md}}
          GH_AW_PROMPT_93fb119cfe9fd910_EOF
          } > "$GH_AW_PROMPT"
      - name: Interpolate variables and render templates
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/interpolate_prompt.cjs');
            await main();
      - name: Substitute placeholders
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
          GH_AW_GITHUB_ACTOR: ${{ github.actor }}
          GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }}
          GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }}
          GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
          GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }}
          GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
          GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
          GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            
            const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs');
            
            // Call the substitution function
            return await substitutePlaceholders({
              file: process.env.GH_AW_PROMPT,
              substitutions: {
                GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR,
                GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID,
                GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER,
                GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER,
                GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER,
                GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY,
                GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID,
                GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE
              }
            });
      - name: Validate prompt placeholders
        env:
          GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
        # poutine:ignore untrusted_checkout_exec
        run: bash ${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh
      - name: Print prompt
        env:
          GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
        # poutine:ignore untrusted_checkout_exec
        run: bash ${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh
      - name: Upload activation artifact
        if: success()
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
        with:
          name: activation
          path: |
            /tmp/gh-aw/aw_info.json
            /tmp/gh-aw/aw-prompts/prompt.txt
            /tmp/gh-aw/github_rate_limits.jsonl
          if-no-files-found: ignore
          retention-days: 1

  agent:
    needs: activation
    runs-on: ubuntu-latest
    permissions:
      contents: read
    env:
      DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
      GH_AW_ASSETS_ALLOWED_EXTS: ""
      GH_AW_ASSETS_BRANCH: ""
      GH_AW_ASSETS_MAX_SIZE_KB: 0
      GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
      GH_AW_WORKFLOW_ID_SANITIZED: helloworld
    outputs:
      checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
      effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }}
      has_patch: ${{ steps.collect_output.outputs.has_patch }}
      inference_access_error: ${{ steps.detect-inference-error.outputs.inference_access_error || 'false' }}
      model: ${{ needs.activation.outputs.model }}
      output: ${{ steps.collect_output.outputs.output }}
      output_types: ${{ steps.collect_output.outputs.output_types }}
      setup-trace-id: ${{ steps.setup.outputs.trace-id }}
    steps:
      - name: Setup Scripts
        id: setup
        uses: github/gh-aw-actions/setup@80471a493be8c528dd27daf73cd644242a7965e0 # v0.67.1
        with:
          destination: ${{ runner.temp }}/gh-aw/actions
          job-name: ${{ github.job }}
          trace-id: ${{ needs.activation.outputs.setup-trace-id }}
      - name: Set runtime paths
        id: set-runtime-paths
        run: |
          echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl" >> "$GITHUB_OUTPUT"
          echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" >> "$GITHUB_OUTPUT"
          echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json" >> "$GITHUB_OUTPUT"
      - name: Checkout repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          persist-credentials: false
      - 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: Configure Git credentials
        env:
          REPO_NAME: ${{ github.repository }}
          SERVER_URL: ${{ github.server_url }}
          GITHUB_TOKEN: ${{ github.token }}
        run: |
          git config --global user.email "github-actions[bot]@users.noreply.github.com"
          git config --global user.name "github-actions[bot]"
          git config --global am.keepcr true
          # Re-authenticate git with GitHub token
          SERVER_URL_STRIPPED="${SERVER_URL#https://}"
          git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
          echo "Git configured with standard GitHub Actions identity"
      - name: Checkout PR branch
        id: checkout-pr
        if: |
          github.event.pull_request || github.event.issue.pull_request
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
        with:
          github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs');
            await main();
      - name: Install GitHub Copilot CLI
        run: ${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh latest
        env:
          GH_HOST: github.com
      - name: Install AWF binary
        run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13
      - name: Determine automatic lockdown mode for GitHub MCP Server
        id: determine-automatic-lockdown
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
          GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
        with:
          script: |
            const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs');
            await determineAutomaticLockdown(github, context, core);
      - name: Download container images
        run: bash ${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.25.13 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.13 ghcr.io/github/gh-aw-firewall/squid:0.25.13 ghcr.io/github/gh-aw-mcpg:v0.2.14 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
      - name: Write Safe Outputs Config
        run: |
          mkdir -p ${RUNNER_TEMP}/gh-aw/safeoutputs
          mkdir -p /tmp/gh-aw/safeoutputs
          mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
          cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_501f7e9d2f201789_EOF'
          {"create_issue":{"labels":["hello-world"],"max":1,"title_prefix":"[hello-world]"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}}
          GH_AW_SAFE_OUTPUTS_CONFIG_501f7e9d2f201789_EOF
      - name: Write Safe Outputs Tools
        run: |
          cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_97f75ab78fe5cc06_EOF'
          {
            "description_suffixes": {
              "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[hello-world]\". Labels [\"hello-world\"] will be automatically added."
            },
            "repo_params": {},
            "dynamic_tools": []
          }
          GH_AW_SAFE_OUTPUTS_TOOLS_META_97f75ab78fe5cc06_EOF
          cat > ${RUNNER_TEMP}/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_dda090d97cc3ffd5_EOF'
          {
            "create_issue": {
              "defaultMax": 1,
              "fields": {
                "body": {
                  "required": true,
                  "type": "string",
                  "sanitize": true,
                  "maxLength": 65000
                },
                "labels": {
                  "type": "array",
                  "itemType": "string",
                  "itemSanitize": true,
                  "itemMaxLength": 128
                },
                "parent": {
                  "issueOrPRNumber": true
                },
                "repo": {
                  "type": "string",
                  "maxLength": 256
                },
                "temporary_id": {
                  "type": "string"
                },
                "title": {
                  "required": true,
                  "type": "string",
                  "sanitize": true,
                  "maxLength": 128
                }
              }
            },
            "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
                }
              }
            }
          }
          GH_AW_SAFE_OUTPUTS_VALIDATION_dda090d97cc3ffd5_EOF
          node ${RUNNER_TEMP}/gh-aw/actions/generate_safe_outputs_tools.cjs
      - name: Generate Safe Outputs MCP Server Config
        id: safe-outputs-config
        run: |
          # Generate a secure random API key (360 bits of entropy, 40+ chars)
          # Mask immediately to prevent timing vulnerabilities
          API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
          echo "::add-mask::${API_KEY}"
          
          PORT=3001
          
          # Set outputs for next steps
          {
            echo "safe_outputs_api_key=${API_KEY}"
            echo "safe_outputs_port=${PORT}"
          } >> "$GITHUB_OUTPUT"
          
          echo "Safe Outputs MCP server will run on port ${PORT}"
          
      - name: Start Safe Outputs MCP HTTP Server
        id: safe-outputs-start
        env:
          DEBUG: '*'
          GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
          GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
          GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
          GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/tools.json
          GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/config.json
          GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
        run: |
          # Environment variables are set above to prevent template injection
          export DEBUG
          export GH_AW_SAFE_OUTPUTS
          export GH_AW_SAFE_OUTPUTS_PORT
          export GH_AW_SAFE_OUTPUTS_API_KEY
          export GH_AW_SAFE_OUTPUTS_TOOLS_PATH
          export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
          export GH_AW_MCP_LOG_DIR
          
          bash ${RUNNER_TEMP}/gh-aw/actions/start_safe_outputs_server.sh
          
      - name: Start MCP Gateway
        id: start-mcp-gateway
        env:
          GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
          GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
          GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
          GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
          GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
          GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
        run: |
          set -eo pipefail
          mkdir -p /tmp/gh-aw/mcp-config
          
          # Export gateway environment variables for MCP config and gateway script
          export MCP_GATEWAY_PORT="80"
          export MCP_GATEWAY_DOMAIN="host.docker.internal"
          MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
          echo "::add-mask::${MCP_GATEWAY_API_KEY}"
          export MCP_GATEWAY_API_KEY
          export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads"
          mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}"
          export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288"
          export DEBUG="*"
          
          export GH_AW_ENGINE="copilot"
          export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -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 ghcr.io/github/gh-aw-mcpg:v0.2.14'
          
          mkdir -p /home/runner/.copilot
          cat << GH_AW_MCP_CONFIG_fa9075c7d60867a1_EOF | bash ${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh
          {
            "mcpServers": {
              "github": {
                "type": "stdio",
                "container": "ghcr.io/github/github-mcp-server:v0.32.0",
                "env": {
                  "GITHUB_HOST": "\${GITHUB_SERVER_URL}",
                  "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
                  "GITHUB_READ_ONLY": "1",
                  "GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
                },
                "guard-policies": {
                  "allow-only": {
                    "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
                    "repos": "$GITHUB_MCP_GUARD_REPOS"
                  }
                }
              },
              "safeoutputs": {
                "type": "http",
                "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
                "headers": {
                  "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
                },
                "guard-policies": {
                  "write-sink": {
                    "accept": [
                      "*"
                    ]
                  }
                }
              }
            },
            "gateway": {
              "port": $MCP_GATEWAY_PORT,
              "domain": "${MCP_GATEWAY_DOMAIN}",
              "apiKey": "${MCP_GATEWAY_API_KEY}",
              "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
            }
          }
          GH_AW_MCP_CONFIG_fa9075c7d60867a1_EOF
      - name: Download activation artifact
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: activation
          path: /tmp/gh-aw
      - name: Clean git credentials
        continue-on-error: true
        run: bash ${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh
      - name: Execute GitHub Copilot CLI
        id: agentic_execution
        # Copilot CLI tool arguments (sorted):
        timeout-minutes: 20
        run: |
          set -o pipefail
          touch /tmp/gh-aw/agent-step-summary.md
          # shellcheck disable=SC1003
          sudo -E awf --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --image-tag 0.25.13 --skip-pull --enable-api-proxy \
            -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
        env:
          COPILOT_AGENT_RUNNER_TYPE: STANDALONE
          COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
          COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }}
          GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json
          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_VERSION: v0.67.1
          GITHUB_API_URL: ${{ github.api_url }}
          GITHUB_AW: true
          GITHUB_HEAD_REF: ${{ github.head_ref }}
          GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
          GITHUB_REF_NAME: ${{ github.ref_name }}
          GITHUB_SERVER_URL: ${{ github.server_url }}
          GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
          GITHUB_WORKSPACE: ${{ github.workspace }}
          GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
          GIT_AUTHOR_NAME: github-actions[bot]
          GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
          GIT_COMMITTER_NAME: github-actions[bot]
          XDG_CONFIG_HOME: /home/runner
      - name: Detect inference access error
        id: detect-inference-error
        if: always()
        continue-on-error: true
        run: bash ${RUNNER_TEMP}/gh-aw/actions/detect_inference_access_error.sh
      - name: Configure Git credentials
        env:
          REPO_NAME: ${{ github.repository }}
          SERVER_URL: ${{ github.server_url }}
          GITHUB_TOKEN: ${{ github.token }}
        run: |
          git config --global user.email "github-actions[bot]@users.noreply.github.com"
          git config --global user.name "github-actions[bot]"
          git config --global am.keepcr true
          # Re-authenticate git with GitHub token
          SERVER_URL_STRIPPED="${SERVER_URL#https://}"
          git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
          echo "Git configured with standard GitHub Actions identity"
      - name: Copy Copilot session state files to logs
        if: always()
        continue-on-error: true
        run: bash ${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh
      - name: Stop MCP Gateway
        if: always()
        continue-on-error: true
        env:
          MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
          MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
          GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
        run: |
          bash ${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID"
      - name: Redact secrets in logs
        if: always()
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/redact_secrets.cjs');
            await main();
        env:
          GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
          SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
          SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
          SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
          SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      - name: Append agent step summary
        if: always()
        run: bash ${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh
      - name: Copy Safe Outputs
        if: always()
        env:
          GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
        run: |
          mkdir -p /tmp/gh-aw
          cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true
      - name: Ingest agent output
        id: collect_output
        if: always()
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
          GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
          GITHUB_SERVER_URL: ${{ github.server_url }}
          GITHUB_API_URL: ${{ github.api_url }}
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/collect_ndjson_output.cjs');
            await main();
      - name: Parse agent logs for step summary
        if: always()
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_copilot_log.cjs');
            await main();
      - name: Parse MCP Gateway logs for step summary
        if: always()
        id: parse-mcp-gateway
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_mcp_gateway_log.cjs');
            await main();
      - name: Print firewall logs
        if: always()
        continue-on-error: true
        env:
          AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs
        run: |
          # Fix permissions on firewall logs so they can be uploaded as artifacts
          # AWF runs with sudo, creating files owned by root
          sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || true
          # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step)
          if command -v awf &> /dev/null; then
            awf logs summary | tee -a "$GITHUB_STEP_SUMMARY"
          else
            echo 'AWF binary not installed, skipping firewall log summary'
          fi
      - name: Parse token usage for step summary
        if: always()
        continue-on-error: true
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_token_usage.cjs');
            await main();
      - name: Write agent output placeholder if missing
        if: always()
        run: |
          if [ ! -f /tmp/gh-aw/agent_output.json ]; then
            echo '{"items":[]}' > /tmp/gh-aw/agent_output.json
          fi
      - name: Upload agent artifacts
        if: always()
        continue-on-error: true
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
        with:
          name: agent
          path: |
            /tmp/gh-aw/aw-prompts/prompt.txt
            /tmp/gh-aw/sandbox/agent/logs/
            /tmp/gh-aw/redacted-urls.log
            /tmp/gh-aw/mcp-logs/
            /tmp/gh-aw/agent_usage.json
            /tmp/gh-aw/agent-stdio.log
            /tmp/gh-aw/agent/
            /tmp/gh-aw/github_rate_limits.jsonl
            /tmp/gh-aw/safeoutputs.jsonl
            /tmp/gh-aw/agent_output.json
            /tmp/gh-aw/aw-*.patch
            /tmp/gh-aw/aw-*.bundle
          if-no-files-found: ignore
      - name: Upload firewall audit logs
        if: always()
        continue-on-error: true
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
        with:
          name: firewall-audit-logs
          path: |
            /tmp/gh-aw/sandbox/firewall/logs/
            /tmp/gh-aw/sandbox/firewall/audit/
          if-no-files-found: ignore

  conclusion:
    needs:
      - activation
      - agent
      - detection
      - safe_outputs
    if: always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true')
    runs-on: ubuntu-slim
    permissions:
      contents: read
      issues: write
    concurrency:
      group: "gh-aw-conclusion-hello-world"
      cancel-in-progress: false
    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@80471a493be8c528dd27daf73cd644242a7965e0 # v0.67.1
        with:
          destination: ${{ runner.temp }}/gh-aw/actions
          job-name: ${{ github.job }}
          trace-id: ${{ needs.activation.outputs.setup-trace-id }}
      - name: Download agent output artifact
        id: download-agent-output
        continue-on-error: true
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: agent
          path: /tmp/gh-aw/
      - name: Setup agent output environment variable
        id: setup-agent-output-env
        if: steps.download-agent-output.outcome == 'success'
        run: |
          mkdir -p /tmp/gh-aw/
          find "/tmp/gh-aw/" -type f -print
          echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
      - name: Process No-Op Messages
        id: noop
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
          GH_AW_NOOP_MAX: "1"
          GH_AW_WORKFLOW_NAME: "Hello World"
          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"
        with:
          github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_noop_message.cjs');
            await main();
      - name: Record Missing Tool
        id: missing_tool
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        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: "Hello World"
        with:
          github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/missing_tool.cjs');
            await main();
      - name: Record Incomplete
        id: report_incomplete
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
          GH_AW_REPORT_INCOMPLETE_CREATE_ISSUE: "true"
          GH_AW_WORKFLOW_NAME: "Hello World"
        with:
          github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/report_incomplete_handler.cjs');
            await main();
      - name: Handle Agent Failure
        id: handle_agent_failure
        if: always()
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
          GH_AW_WORKFLOW_NAME: "Hello World"
          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: "hello-world"
          GH_AW_ENGINE_ID: "copilot"
          GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }}
          GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
          GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }}
          GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }}
          GH_AW_GROUP_REPORTS: "false"
          GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
          GH_AW_TIMEOUT_MINUTES: "20"
        with:
          github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/handle_agent_failure.cjs');
            await main();

  detection:
    needs:
      - activation
      - agent
    if: >
      always() && needs.agent.result != 'skipped' && (needs.agent.outputs.output_types != '' || needs.agent.outputs.has_patch == 'true')
    runs-on: ubuntu-latest
    permissions:
      contents: read
    outputs:
      detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
      detection_success: ${{ steps.detection_conclusion.outputs.success }}
    steps:
      - name: Setup Scripts
        id: setup
        uses: github/gh-aw-actions/setup@80471a493be8c528dd27daf73cd644242a7965e0 # v0.67.1
        with:
          destination: ${{ runner.temp }}/gh-aw/actions
          job-name: ${{ github.job }}
          trace-id: ${{ needs.activation.outputs.setup-trace-id }}
      - name: Download agent output artifact
        id: download-agent-output
        continue-on-error: true
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: agent
          path: /tmp/gh-aw/
      - name: Setup agent output environment variable
        id: setup-agent-output-env
        if: steps.download-agent-output.outcome == 'success'
        run: |
          mkdir -p /tmp/gh-aw/
          find "/tmp/gh-aw/" -type f -print
          echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
      - name: Checkout repository for patch context
        if: needs.agent.outputs.has_patch == 'true'
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          persist-credentials: false
      # --- Threat Detection ---
      - name: Download container images
        run: bash ${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.25.13 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.13 ghcr.io/github/gh-aw-firewall/squid:0.25.13
      - 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 configuration for detection
        if: always() && steps.detection_guard.outputs.run_detection == 'true'
        run: |
          rm -f /tmp/gh-aw/mcp-config/mcp-servers.json
          rm -f /home/runner/.copilot/mcp-config.json
          rm -f "$GITHUB_WORKSPACE/.gemini/settings.json"
      - name: Prepare threat detection files
        if: always() && steps.detection_guard.outputs.run_detection == 'true'
        run: |
          mkdir -p /tmp/gh-aw/threat-detection/aw-prompts
          cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true
          cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true
          for f in /tmp/gh-aw/aw-*.patch; do
            [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
          done
          for f in /tmp/gh-aw/aw-*.bundle; do
            [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
          done
          echo "Prepared threat detection files:"
          ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true
      - name: Setup threat detection
        if: always() && steps.detection_guard.outputs.run_detection == 'true'
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          WORKFLOW_NAME: "Hello World"
          WORKFLOW_DESCRIPTION: "hello world"
          HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/setup_threat_detection.cjs');
            await main();
      - name: Ensure threat-detection directory and log
        if: always() && steps.detection_guard.outputs.run_detection == 'true'
        run: |
          mkdir -p /tmp/gh-aw/threat-detection
          touch /tmp/gh-aw/threat-detection/detection.log
      - name: Install GitHub Copilot CLI
        run: ${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh latest
        env:
          GH_HOST: github.com
      - name: Install AWF binary
        run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13
      - name: Execute GitHub Copilot CLI
        if: always() && steps.detection_guard.outputs.run_detection == 'true'
        id: detection_agentic_execution
        # Copilot CLI tool arguments (sorted):
        timeout-minutes: 20
        run: |
          set -o pipefail
          touch /tmp/gh-aw/agent-step-summary.md
          # shellcheck disable=SC1003
          sudo -E awf --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,telemetry.enterprise.githubcopilot.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --image-tag 0.25.13 --skip-pull --enable-api-proxy \
            -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
        env:
          COPILOT_AGENT_RUNNER_TYPE: STANDALONE
          COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
          COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || '' }}
          GH_AW_PHASE: detection
          GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
          GH_AW_VERSION: v0.67.1
          GITHUB_API_URL: ${{ github.api_url }}
          GITHUB_AW: true
          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]
          XDG_CONFIG_HOME: /home/runner
      - name: Upload threat detection log
        if: always() && steps.detection_guard.outputs.run_detection == 'true'
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
        with:
          name: detection
          path: /tmp/gh-aw/threat-detection/detection.log
          if-no-files-found: ignore
      - name: Parse and conclude threat detection
        id: detection_conclusion
        if: always()
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }}
        with:
          script: |
            const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/parse_threat_detection_results.cjs');
            await main();

  safe_outputs:
    needs:
      - activation
      - agent
      - detection
    if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success'
    runs-on: ubuntu-slim
    permissions:
      contents: read
      issues: write
    timeout-minutes: 15
    env:
      GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/hello-world"
      GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }}
      GH_AW_ENGINE_ID: "copilot"
      GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }}
      GH_AW_WORKFLOW_ID: "hello-world"
      GH_AW_WORKFLOW_NAME: "Hello World"
    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 }}
      created_issue_number: ${{ steps.process_safe_outputs.outputs.created_issue_number }}
      created_issue_url: ${{ steps.process_safe_outputs.outputs.created_issue_url }}
      process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
      process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
    steps:
      - name: Setup Scripts
        id: setup
        uses: github/gh-aw-actions/setup@80471a493be8c528dd27daf73cd644242a7965e0 # v0.67.1
        with:
          destination: ${{ runner.temp }}/gh-aw/actions
          job-name: ${{ github.job }}
          trace-id: ${{ needs.activation.outputs.setup-trace-id }}
      - name: Download agent output artifact
        id: download-agent-output
        continue-on-error: true
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: agent
          path: /tmp/gh-aw/
      - name: Setup agent output environment variable
        id: setup-agent-output-env
        if: steps.download-agent-output.outcome == 'success'
        run: |
          mkdir -p /tmp/gh-aw/
          find "/tmp/gh-aw/" -type f -print
          echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
      - name: Configure GH_HOST for enterprise compatibility
        id: ghes-host-config
        shell: bash
        run: |
          # 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@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
          GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
          GITHUB_SERVER_URL: ${{ github.server_url }}
          GITHUB_API_URL: ${{ github.api_url }}
          GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"labels\":[\"hello-world\"],\"max\":1,\"title_prefix\":\"[hello-world]\"},\"create_report_incomplete_issue\":{},\"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 { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
            setupGlobals(core, github, context, exec, io);
            const { main } = require('${{ runner.temp }}/gh-aw/actions/safe_output_handler_manager.cjs');
            await main();
      - name: Upload Safe Output Items
        if: always()
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
        with:
          name: safe-output-items
          path: /tmp/gh-aw/safe-output-items.jsonl
          if-no-files-found: ignore

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions