Skip to content

Fix infinite recursion in project discovery on symlink cycles #382

Fix infinite recursion in project discovery on symlink cycles

Fix infinite recursion in project discovery on symlink cycles #382

# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"65c3508dca9c4002dbf33280b955cb2e7ae7f2e8fd7c15dccae31e664e70158f","body_hash":"02f592e5642f222b8d47478b9658a6fe3734714b3d4bf4abfadd799312449cb1","compiler_version":"v0.87.5-197-g76f6ea7c22","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.80"}}
# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","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/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw/actions/setup","sha":"76f6ea7c222034dcb6282848ba08935d75df1b8f","version":"76f6ea7c222034dcb6282848ba08935d75df1b8f"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.28.9","digest":"sha256:54b2fb3068efc15a4cc1bd4033f8fa056a9b1779baeba0cb80ae95ea55e7e343","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.28.9@sha256:54b2fb3068efc15a4cc1bd4033f8fa056a9b1779baeba0cb80ae95ea55e7e343"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.9","digest":"sha256:a0ffb1dc926c6e5a500b336893e032a8f167d3db43c869be886874ef14280bb8","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.28.9@sha256:a0ffb1dc926c6e5a500b336893e032a8f167d3db43c869be886874ef14280bb8"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.28.9","digest":"sha256:3d5dba0b0a139bbb11b5d5b8b44f277d2b18f69cf43090e3c283d750cf864baa","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.28.9@sha256:3d5dba0b0a139bbb11b5d5b8b44f277d2b18f69cf43090e3c283d750cf864baa"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.13","digest":"sha256:ec4008521c610e1113ed557ecec0ff64a2c2111e4cfa817bab54d9b7da24c7cc","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.13@sha256:ec4008521c610e1113ed557ecec0ff64a2c2111e4cfa817bab54d9b7da24c7cc"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:bac2192f6374d6262116399b34fc5e143d576f82719e90a18261cae7480f4d4e","pinned_image":"ghcr.io/github/gh-aw-node@sha256:bac2192f6374d6262116399b34fc5e143d576f82719e90a18261cae7480f4d4e"},{"image":"ghcr.io/github/github-mcp-server:v1.11.0","digest":"sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699","pinned_image":"ghcr.io/github/github-mcp-server:v1.11.0@sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699"}],"memory_validation_scripts":[{"memory":"repo-memory:default","sha256":"a236eed07c8ad4b6bf3a1af5daed796608ddb4f3a7f799c8429fc48b620965d2"}],"mcp_servers":[{"name":"github","tools":["actions_get","actions_list","get_code_scanning_alert","get_commit","get_discussion","get_discussion_comments","get_file_contents","get_job_logs","get_label","get_latest_release","get_me","get_notification_details","get_pull_request","get_pull_request_comments","get_pull_request_diff","get_pull_request_files","get_pull_request_review_comments","get_pull_request_reviews","get_pull_request_status","get_release_by_tag","get_secret_scanning_alert","get_tag","issue_read","list_branches","list_code_scanning_alerts","list_commits","list_discussion_categories","list_discussions","list_issue_types","list_issues","list_label","list_notifications","list_pull_requests","list_releases","list_secret_scanning_alerts","list_starred_repositories","list_tags","pull_request_read","search_code","search_issues","search_orgs","search_pull_requests","search_repositories","search_users"]},{"name":"safeoutputs","tools":["add_comment","add_labels","create_issue","create_pull_request","missing_data","missing_tool","noop","push_to_pull_request_branch","remove_labels","update_issue"]}]}
# This file was automatically generated by gh-aw. 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 githubnext/agentics/workflows/repo-assist.md@ae8d551f07c7ed7619f8c58c7bb4c3ac89395d38 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/
#
# A friendly repository assistant that runs regularly (twice a day by default) to assist maintainers.
# Can also be triggered on-demand via '/repo-assist <instructions>' to perform specific tasks.
# - Labels and triages open issues
# - Investigates open issues and resolves, fixes, clarifies, or comments on them
# - Identifies issues that can be fixed and creates draft pull requests with fixes
# - Improves performance, testing, and code quality via PRs
# - Makes engineering investments: dependency updates, CI improvements, tooling
# - Updates its own PRs when CI fails or merge conflicts arise
# - Improves documentation, performs ad hoc QA, and maintains project basics
# - Takes the repository forward with proactive improvements
# - Maintains a persistent memory of work done and what remains
# Always polite, constructive, and mindful of the project's goals.
#
# Source: githubnext/agentics/workflows/repo-assist.md@ae8d551f07c7ed7619f8c58c7bb4c3ac89395d38
#
# Secrets used:
# - COPILOT_GITHUB_TOKEN
# - GH_AW_CI_TRIGGER_TOKEN
# - 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/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9)
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
# - github/gh-aw/actions/setup@76f6ea7c222034dcb6282848ba08935d75df1b8f # 76f6ea7c222034dcb6282848ba08935d75df1b8f
#
# Container images used:
# - ghcr.io/github/gh-aw-firewall/agent:0.28.9@sha256:54b2fb3068efc15a4cc1bd4033f8fa056a9b1779baeba0cb80ae95ea55e7e343
# - ghcr.io/github/gh-aw-firewall/api-proxy:0.28.9@sha256:a0ffb1dc926c6e5a500b336893e032a8f167d3db43c869be886874ef14280bb8
# - ghcr.io/github/gh-aw-firewall/squid:0.28.9@sha256:3d5dba0b0a139bbb11b5d5b8b44f277d2b18f69cf43090e3c283d750cf864baa
# - ghcr.io/github/gh-aw-mcpg:v0.4.13@sha256:ec4008521c610e1113ed557ecec0ff64a2c2111e4cfa817bab54d9b7da24c7cc
# - ghcr.io/github/gh-aw-node@sha256:bac2192f6374d6262116399b34fc5e143d576f82719e90a18261cae7480f4d4e
# - ghcr.io/github/github-mcp-server:v1.11.0@sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699
name: "Repo Assist"
on:
discussion:
types:
- created
- edited
discussion_comment:
types:
- created
- edited
issue_comment:
types:
- created
- edited
issues:
types:
- opened
- edited
- reopened
# permissions: # Permissions applied to pre-activation job
# pull-requests: read
pull_request:
types:
- opened
- edited
- reopened
pull_request_review_comment:
types:
- created
- edited
schedule:
- cron: "44 */1 * * *"
# steps: # Steps injected into pre-activation job
# - env:
# GH_TOKEN: ${{ github.token }}
# id: check
# run: |
# MAX_OPEN_PRS=8
# if [[ "$GITHUB_EVENT_NAME" != "schedule" ]]; then exit 0; fi
# COUNT=$(gh pr list --repo "$GITHUB_REPOSITORY" --state open --search 'in:title "[repo-assist]"' --json number --jq 'length')
# [[ "$COUNT" -lt "$MAX_OPEN_PRS" ]]
workflow_dispatch:
inputs:
aw_context:
default: ""
description: "Agent caller context (used internally by Agentic Workflows)."
required: false
type: string
command:
default: ""
description: "Optional command-mode instruction (for example: Run Task 9)"
required: false
type: string
permissions: {}
concurrency:
group: "gh-aw-${{ github.workflow }}-${{ github.event.issue.number || github.event.pull_request.number || github.run_id }}"
queue: max
run-name: "Repo Assist"
env:
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ vars.GH_AW_DEFAULT_OTLP_ENDPOINT }}
OTEL_SERVICE_NAME: gh-aw.repo-assist
OTEL_RESOURCE_ATTRIBUTES: 'gh-aw.workflow.name=Repo%20Assist,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' && ((needs.pre_activation.outputs.check_result == 'success') && ((github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment') && (github.event_name == 'issues' && (startsWith(github.event.issue.body, '/repo-assist ') || startsWith(github.event.issue.body, '/repo-assist\n') || github.event.issue.body == '/repo-assist') || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/repo-assist ') || startsWith(github.event.comment.body, '/repo-assist\n') || github.event.comment.body == '/repo-assist') && github.event.issue.pull_request == null || github.event_name == 'issue_comment' && (startsWith(github.event.comment.body, '/repo-assist ') || startsWith(github.event.comment.body, '/repo-assist\n') || github.event.comment.body == '/repo-assist') && github.event.issue.pull_request != null || github.event_name == 'pull_request_review_comment' && (startsWith(github.event.comment.body, '/repo-assist ') || startsWith(github.event.comment.body, '/repo-assist\n') || github.event.comment.body == '/repo-assist') || github.event_name == 'pull_request' && (startsWith(github.event.pull_request.body, '/repo-assist ') || startsWith(github.event.pull_request.body, '/repo-assist\n') || github.event.pull_request.body == '/repo-assist') || github.event_name == 'discussion' && (startsWith(github.event.discussion.body, '/repo-assist ') || startsWith(github.event.discussion.body, '/repo-assist\n') || github.event.discussion.body == '/repo-assist') || github.event_name == 'discussion_comment' && (startsWith(github.event.comment.body, '/repo-assist ') || startsWith(github.event.comment.body, '/repo-assist\n') || github.event.comment.body == '/repo-assist')) || !(github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment')))"
runs-on: ubuntu-slim
permissions:
actions: read
contents: read
discussions: write
issues: write
pull-requests: write
env:
GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }}
GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
outputs:
body: ${{ steps.sanitized.outputs.body }}
comment_id: ${{ steps.add-comment.outputs.comment-id }}
comment_repo: ${{ steps.add-comment.outputs.comment-repo }}
comment_url: ${{ steps.add-comment.outputs.comment-url }}
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' }}
secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
setup-span-id: ${{ steps.setup.outputs.span-id }}
setup-trace-id: ${{ steps.setup.outputs.trace-id }}
slash_command: ${{ needs.pre_activation.outputs.matched_command }}
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@76f6ea7c222034dcb6282848ba08935d75df1b8f # 76f6ea7c222034dcb6282848ba08935d75df1b8f
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: "Repo Assist"
GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/repo-assist.lock.yml@${{ github.ref }}
GH_AW_INFO_VERSION: "1.0.80"
GH_AW_INFO_AWF_VERSION: "v0.28.9"
GH_AW_INFO_BODY_MODIFIED: "false"
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: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'auto' }}
GH_AW_INFO_VERSION: "1.0.80"
GH_AW_INFO_AGENT_VERSION: "1.0.80"
GH_AW_INFO_WORKFLOW_NAME: "Repo Assist"
GH_AW_INFO_EXPERIMENTAL: "false"
GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
GH_AW_INFO_STAGED: "false"
GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","dotnet","node","python","rust","java","github","ci.appveyor.com","appveyorcidata.blob.core.windows.net"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
GH_AW_INFO_AWF_VERSION: "v0.28.9"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_INFO_AGENT_RUNTIME: ""
GH_AW_INFO_FRONTMATTER_SOURCE: "githubnext/agentics/workflows/repo-assist.md@ae8d551f07c7ed7619f8c58c7bb4c3ac89395d38"
GH_AW_INFO_BODY_MODIFIED: "false"
GH_AW_COMPILED_STRICT: "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-repoassist-${{ github.run_id }}
restore-keys: agentic-workflow-usage-repoassist-
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: "Repo Assist"
GH_AW_WORKFLOW_ID: "repo-assist"
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: "true"
GH_AW_HAS_LABEL_COMMAND: "false"
GH_AW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_AW_MAX_DAILY_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_DAILY_AI_CREDITS || '5000' }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const 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: Add eyes reaction for immediate feedback
id: react
if: github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment' || github.event_name == 'pull_request' && github.event.pull_request.head.repo.id == github.repository_id
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_AW_REACTION: "eyes"
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, 'add_reaction.cjs'));
await main();
- name: Validate COPILOT_GITHUB_TOKEN secret
id: validate-secret
run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_multi_secret.sh" COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- name: Check for OAuth tokens
id: check-oauth-tokens
run: bash "${RUNNER_TEMP}/gh-aw/actions/check_oauth_tokens.sh"
env:
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
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: "repo-assist.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: Compute current body text
id: sanitized
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.gradle-enterprise.cloud,*.pythonhosted.org,*.vsblob.vsassets.io,adoptium.net,anaconda.org,api.adoptium.net,api.foojay.io,api.npms.io,api.nuget.org,api.snapcraft.io,appveyorcidata.blob.core.windows.net,archive.apache.org,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,binstar.org,bootstrap.pypa.io,builds.dotnet.microsoft.com,bun.sh,cdn.azul.com,cdn.jsdelivr.net,central.sonatype.com,ci.appveyor.com,ci.dot.net,codeload.github.com,conda.anaconda.org,conda.binstar.org,crates.io,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,dc.services.visualstudio.com,deb.nodesource.com,deno.land,develocity.apache.org,dist.nuget.org,dl.google.com,dlcdn.apache.org,docs.github.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,download.eclipse.org,download.java.net,download.oracle.com,downloads.gradle-dn.com,esm.sh,files.pythonhosted.org,ge.spockframework.org,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,googleapis.deno.dev,googlechromelabs.github.io,gradle.org,index.crates.io,jcenter.bintray.com,jdk.java.net,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,maven-central.storage-download.googleapis.com,maven.apache.org,maven.google.com,maven.oracle.com,maven.pkg.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,patchdiff.githubusercontent.com,pip.pypa.io,pkgs.dev.azure.com,plugins-artifacts.gradle.org,plugins.gradle.org,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.gradle.org,repo.grails.org,repo.maven.apache.org,repo.spring.io,repo.yarnpkg.com,repo1.maven.org,repository.apache.org,s.symcb.com,s.symcd.com,scans-in.gradle.com,security.ubuntu.com,services.gradle.org,sh.rustup.rs,skimdb.npmjs.com,static.crates.io,static.rust-lang.org,storage.googleapis.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.java.com,www.microsoft.com,www.npmjs.com,www.npmjs.org,yarnpkg.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: Add comment with workflow run link
id: add-comment
if: github.event_name == 'issues' || github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' || github.event_name == 'discussion' || github.event_name == 'discussion_comment' || github.event_name == 'pull_request' && github.event.pull_request.head.repo.id == github.repository_id
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_AW_WORKFLOW_NAME: "Repo Assist"
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e Generated by 🌈 {workflow_name}, see [workflow run]({run_url}). [Learn more](https://github.com/githubnext/agentics/blob/main/docs/repo-assist.md).\",\"runStarted\":\"{workflow_name} is processing {event_type}, see [workflow run]({run_url})...\",\"runSuccess\":\"✓ {workflow_name} completed successfully, see [workflow run]({run_url}).\",\"runFailure\":\"✗ {workflow_name} encountered {status}, see [workflow run]({run_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, 'add_workflow_run_comment.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\":\"repo_memory_prompt.md\"},{\"file\":\"safe_outputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0001\"},{\"file\":\"safe_outputs_create_pull_request.md\"},{\"file\":\"safe_outputs_push_to_pr_branch.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0002\"},{\"file\":\"mcp_cli_tools_with_safeoutputs_prompt.md\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0003\"},{\"file\":\"github_mcp_tools_with_safeoutputs_prompt.md\"},{\"file\":\"pr_context_prompt.md\",\"condition_env\":\"GH_AW_INCLUDE_PR_CONTEXT\"},{\"file\":\"pr_context_push_to_pr_branch_guidance.md\",\"condition_env\":\"GH_AW_INCLUDE_PR_CONTEXT\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0004\"},{\"content_env\":\"GH_AW_PROMPT_CONTENT_0005\"}]}"
GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
GH_AW_EXPR_1DD9F1B7: ${{ steps.sanitized.outputs.text || inputs.command }}
GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
GH_AW_GITHUB_ACTOR: ${{ github.actor }}
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_SERVER_URL: ${{ github.server_url }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_INCLUDE_PR_CONTEXT: ${{ (github.event_name == 'issue_comment' && github.event.issue.pull_request != null) || github.event_name == 'pull_request_review_comment' || github.event_name == 'pull_request_review' }}
GH_AW_INPUTS_COMMAND: ${{ inputs.command }}
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
GH_AW_WIKI_NOTE: ${{ '' }}
GH_AW_PROMPT_CONTENT_0000: "<system>\n"
GH_AW_PROMPT_CONTENT_0001: "<safe-output-tools>\nTools: add_comment(max:10), create_issue(max:4), update_issue, create_pull_request(max:4), add_labels(max:30), remove_labels(max:5), push_to_pull_request_branch(max:4), missing_tool, missing_data, noop\n"
GH_AW_PROMPT_CONTENT_0002: "</safe-output-tools>\n"
GH_AW_PROMPT_CONTENT_0003: "<github-context>\nThe following GitHub context information is available for this workflow:\n{{#if github.actor}}\n- **actor**: __GH_AW_GITHUB_ACTOR__\n{{/if}}\n{{#if github.repository}}\n- **repository**: __GH_AW_GITHUB_REPOSITORY__\n{{/if}}\n{{#if github.workspace}}\n- **workspace**: __GH_AW_GITHUB_WORKSPACE__\n{{/if}}\n{{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}}\n- **issue-number**: #__GH_AW_EXPR_802A9F6A__\n{{/if}}\n{{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}}\n- **discussion-number**: #__GH_AW_EXPR_1A3A194A__\n{{/if}}\n{{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}}\n- **pull-request-number**: #__GH_AW_EXPR_463A214A__\n{{/if}}\n{{#if github.event.comment.id || github.aw.context.comment_id}}\n- **comment-id**: __GH_AW_EXPR_FF1D34CE__\n{{/if}}\n{{#if github.run_id}}\n- **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__\n{{/if}}\n- **checkouts**: The following repositories have been checked out and are available in the workspace:\n - repo `__GH_AW_GITHUB_REPOSITORY__` → `$GITHUB_WORKSPACE` (cwd) [full history, all branches available as remote-tracking refs] [additional refs fetched: *]\n - **Note**: If a branch you need is not in the list above and is not listed as an additional fetched ref, it has NOT been checked out. For private repositories you cannot fetch it. If the branch is required and not available, exit with an error and ask the user to add it to the `fetch:` option of the `checkout:` configuration (e.g., `fetch: [\"refs/pulls/open/*\"]` for all open PR refs, or `fetch: [\"main\", \"feature/my-branch\"]` for specific branches).\n - **Warning: No git credentials are available to the agent.** Credentials are\n intentionally removed after the checkout step for security. This means any git\n operation that needs to authenticate to the remote will fail. In private repositories, that includes:\n - `git fetch`, `git pull`, `git clone`, and `git push` (direct push, not via safe-output tools)\n - Checking out or switching to a remote branch that is not already fetched\n - Deepening a shallow clone (`git fetch --unshallow`)\n - On-demand blob fetches in partial/blobless clones (operations on files not in the initial checkout)\n Do NOT attempt to configure credentials, run `git credential fill`, or modify `.gitconfig` —\n authentication will not succeed. If you encounter credential prompts or authentication errors,\n stop immediately and report the limitation rather than spending turns trying to work around it.\n</github-context>\n\n"
GH_AW_PROMPT_CONTENT_0004: "</system>\n"
GH_AW_PROMPT_CONTENT_0005: "{{#runtime-import .github/workflows/repo-assist.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"
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_SERVER_URL: ${{ github.server_url }}
GH_AW_INPUTS_COMMAND: ${{ inputs.command }}
GH_AW_EXPR_1DD9F1B7: ${{ steps.sanitized.outputs.text || inputs.command }}
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
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_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
GH_AW_EXPR_1DD9F1B7: ${{ steps.sanitized.outputs.text || inputs.command }}
GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
GH_AW_GITHUB_ACTOR: ${{ github.actor }}
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_SERVER_URL: ${{ github.server_url }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_INCLUDE_PR_CONTEXT: ${{ (github.event_name == 'issue_comment' && github.event.issue.pull_request != null) || github.event_name == 'pull_request_review_comment' || github.event_name == 'pull_request_review' }}
GH_AW_INPUTS_COMMAND: ${{ inputs.command }}
GH_AW_MCP_CLI_SERVERS_LIST: "- `github` — run `github --help` to see available tools\n- `safeoutputs` — run `safeoutputs --help` to see available tools"
GH_AW_MEMORY_BRANCH_NAME: 'memory/repo-assist'
GH_AW_MEMORY_CONSTRAINTS: "\n\n**Constraints:**\n- **Max File Size**: 65536 bytes (0.06 MB) per file\n- **Max File Count**: 1 files per commit\n- **Max Patch Size**: 65536 bytes (64 KB) total per push (max: 1024 KB)\n"
GH_AW_MEMORY_DESCRIPTION: ''
GH_AW_MEMORY_DIR: '/tmp/gh-aw/repo-memory/default/'
GH_AW_MEMORY_TARGET_REPO: ' of the current repository'
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_MATCHED_COMMAND: ${{ needs.pre_activation.outputs.matched_command }}
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: ${{ steps.sanitized.outputs.text }}
GH_AW_WIKI_NOTE: ''
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_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A,
GH_AW_EXPR_1DD9F1B7: process.env.GH_AW_EXPR_1DD9F1B7,
GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A,
GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A,
GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE,
GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR,
GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY,
GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID,
GH_AW_GITHUB_SERVER_URL: process.env.GH_AW_GITHUB_SERVER_URL,
GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE,
GH_AW_INCLUDE_PR_CONTEXT: process.env.GH_AW_INCLUDE_PR_CONTEXT,
GH_AW_INPUTS_COMMAND: process.env.GH_AW_INPUTS_COMMAND,
GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST,
GH_AW_MEMORY_BRANCH_NAME: process.env.GH_AW_MEMORY_BRANCH_NAME,
GH_AW_MEMORY_CONSTRAINTS: process.env.GH_AW_MEMORY_CONSTRAINTS,
GH_AW_MEMORY_DESCRIPTION: process.env.GH_AW_MEMORY_DESCRIPTION,
GH_AW_MEMORY_DIR: process.env.GH_AW_MEMORY_DIR,
GH_AW_MEMORY_TARGET_REPO: process.env.GH_AW_MEMORY_TARGET_REPO,
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED,
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_MATCHED_COMMAND: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_MATCHED_COMMAND,
GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT: process.env.GH_AW_STEPS_SANITIZED_OUTPUTS_TEXT,
GH_AW_WIKI_NOTE: process.env.GH_AW_WIKI_NOTE
}
});
- 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: read-all
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: repoassist
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@76f6ea7c222034dcb6282848ba08935d75df1b8f # 76f6ea7c222034dcb6282848ba08935d75df1b8f
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: "Repo Assist"
GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/repo-assist.lock.yml@${{ github.ref }}
GH_AW_INFO_VERSION: "1.0.80"
GH_AW_INFO_AWF_VERSION: "v0.28.9"
GH_AW_INFO_BODY_MODIFIED: "false"
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
fetch-depth: 0
- name: Fetch additional refs
env:
GH_AW_FETCH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
header=$(printf "x-access-token:%s" "${GH_AW_FETCH_TOKEN}" | base64 -w 0)
git -c "http.extraheader=Authorization: Basic ${header}" fetch origin '+refs/heads/*:refs/remotes/origin/*'
- 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: Start DIFC Proxy
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_HOST: ${{ env.GH_HOST }}
GITHUB_HOST: ${{ env.GITHUB_HOST }}
GITHUB_ENTERPRISE_HOST: ${{ env.GITHUB_ENTERPRISE_HOST }}
GITHUB_GRAPHQL_URL: ${{ env.GITHUB_GRAPHQL_URL }}
GITHUB_COPILOT_BASE_URL: ${{ env.GITHUB_COPILOT_BASE_URL }}
GH_AW_NETWORK_ISOLATION: 'true'
DIFC_PROXY_POLICY: '{"allow-only":{"min-integrity":"none","repos":"all"}}'
DIFC_PROXY_IMAGE: 'ghcr.io/github/gh-aw-mcpg:v0.4.13'
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/start_difc_proxy.sh"
- name: Download activation artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: activation
path: /tmp/gh-aw
# Repo memory git-based storage configuration from frontmatter processed below
- name: Clone repo-memory branch (default)
env:
GH_TOKEN: ${{ github.token }}
GITHUB_SERVER_URL: ${{ github.server_url }}
BRANCH_NAME: memory/repo-assist
TARGET_REPO: ${{ github.repository }}
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
CREATE_ORPHAN: true
run: bash "${RUNNER_TEMP}/gh-aw/actions/clone_repo_memory_branch.sh"
- name: Fetch repo data for task weighting
run: |-
mkdir -p /tmp/gh-aw
# Fetch open issues with labels (up to 500)
gh issue list --state open --limit 500 --json number,labels > /tmp/gh-aw/issues.json
# Fetch open PRs with titles (up to 200)
gh pr list --state open --limit 200 --json number,title > /tmp/gh-aw/prs.json
# Compute task weights and select three tasks for this run
python3 - << 'EOF'
import json, random, os
with open('/tmp/gh-aw/issues.json') as f:
issues = json.load(f)
with open('/tmp/gh-aw/prs.json') as f:
prs = json.load(f)
open_issues = len(issues)
unlabelled = sum(1 for i in issues if not i.get('labels'))
repo_assist_prs = sum(1 for p in prs if p['title'].startswith('[repo-assist]'))
task_names = {
1: 'Issue Labelling',
2: 'Issue Investigation then Resolve, Fix, Seek Clarification or Comment',
3: 'Issue Investigation and Fix',
4: 'Engineering Investments',
5: 'Coding Improvements',
6: 'Maintain Repo Assist PRs',
7: 'Documentation Improvements, Ad Hoc QA, and Project Basics',
8: 'Performance Improvements',
9: 'Testing Improvements',
10: 'Take the Repository Forward',
}
weights = {
1: 1 + 3 * unlabelled,
2: 3 + 1 * open_issues,
3: 3 + 0.7 * open_issues,
4: 5 + 0.2 * open_issues,
5: 5 + 0.1 * open_issues,
6: float(repo_assist_prs),
7: 3 + 0.05 * open_issues,
8: 3 + 0.05 * open_issues,
9: 3 + 0.05 * open_issues,
10: 3 + 0.05 * open_issues,
}
# Seed with run ID for reproducibility within a run
run_id = int(os.environ.get('GITHUB_RUN_ID', '0'))
rng = random.Random(run_id)
task_ids = list(weights.keys())
task_weights = [weights[t] for t in task_ids]
# Weighted sample without replacement (pick 3 distinct tasks)
NUM_TASKS_PER_RUN = 3
chosen, seen = [], set()
for t in rng.choices(task_ids, weights=task_weights, k=30):
if t not in seen:
seen.add(t)
chosen.append(t)
if len(chosen) == NUM_TASKS_PER_RUN:
break
print('=== Repo Assist Task Selection ===')
print(f'Open issues : {open_issues}')
print(f'Unlabelled issues : {unlabelled}')
print(f'Repo Assist PRs : {repo_assist_prs}')
print()
print('Task weights:')
for t, w in weights.items():
tag = ' <-- SELECTED' if t in chosen else ''
print(f' Task {t:2d} ({task_names[t]}): weight {w:6.1f}{tag}')
print()
print(f'Selected tasks for this run: ' + ', '.join(f'Task {c} ({task_names[c]})' for c in chosen))
result = {
'open_issues': open_issues, 'unlabelled_issues': unlabelled,
'repo_assist_prs': repo_assist_prs,
'task_names': task_names,
'weights': {str(k): round(v, 2) for k, v in weights.items()},
'selected_tasks': chosen,
}
with open('/tmp/gh-aw/task_selection.json', 'w') as f:
json.dump(result, f, indent=2)
EOF
env:
GH_HOST: ${{ env.GH_HOST || 'github.com' }}
GH_REPO: ${{ github.repository }}
GH_TOKEN: ${{ github.token }}
GITHUB_API_URL: https://localhost:18443/api/v3
GITHUB_GRAPHQL_URL: https://localhost:18443/api/graphql
NODE_EXTRA_CA_CERTS: /tmp/gh-aw/proxy-logs/proxy-tls/ca.crt
- 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: dev
- name: Install AWF binary
run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.28.9 --rootless
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9)
env:
GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
GH_AW_GITHUB_MIN_INTEGRITY: 'none'
with:
script: |
const path = require('path');
const actionsDir = path.join(process.env.RUNNER_TEMP, 'gh-aw', 'actions');
const determineAutomaticLockdown = require(path.join(actionsDir, 'determine_automatic_lockdown.cjs'));
await determineAutomaticLockdown(github, context, core);
- name: Parse integrity filter lists
id: parse-guard-vars
env:
GH_AW_BLOCKED_USERS_VAR: ${{ vars.GH_AW_GITHUB_BLOCKED_USERS || '' }}
GH_AW_TRUSTED_USERS_VAR: ${{ vars.GH_AW_GITHUB_TRUSTED_USERS || '' }}
GH_AW_APPROVAL_LABELS_VAR: ${{ vars.GH_AW_GITHUB_APPROVAL_LABELS || '' }}
run: bash "${RUNNER_TEMP}/gh-aw/actions/parse_guard_list.sh"
- name: Stop DIFC Proxy
if: always()
continue-on-error: true
run: bash "${RUNNER_TEMP}/gh-aw/actions/stop_difc_proxy.sh"
- 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.9@sha256:54b2fb3068efc15a4cc1bd4033f8fa056a9b1779baeba0cb80ae95ea55e7e343 ghcr.io/github/gh-aw-firewall/api-proxy:0.28.9@sha256:a0ffb1dc926c6e5a500b336893e032a8f167d3db43c869be886874ef14280bb8 ghcr.io/github/gh-aw-firewall/squid:0.28.9@sha256:3d5dba0b0a139bbb11b5d5b8b44f277d2b18f69cf43090e3c283d750cf864baa ghcr.io/github/gh-aw-mcpg:v0.4.13@sha256:ec4008521c610e1113ed557ecec0ff64a2c2111e4cfa817bab54d9b7da24c7cc ghcr.io/github/gh-aw-node@sha256:bac2192f6374d6262116399b34fc5e143d576f82719e90a18261cae7480f4d4e ghcr.io/github/github-mcp-server:v1.11.0@sha256:fbec75de11c255213fa08d80fb166abe73d851fff631c51c0079872967720699
- 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: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":10,\"target\":\"*\"},\"add_labels\":{\"allowed\":[\"bug\",\"enhancement\",\"help wanted\",\"good first issue\",\"spam\",\"off topic\",\"documentation\",\"question\",\"duplicate\",\"wontfix\",\"needs triage\",\"needs investigation\",\"breaking change\",\"performance\",\"security\",\"refactor\"],\"max\":30,\"target\":\"*\"},\"create_issue\":{\"labels\":[\"automation\",\"repo-assist\"],\"max\":4,\"title_prefix\":\"[repo-assist] \"},\"create_pull_request\":{\"draft\":true,\"labels\":[\"automation\",\"repo-assist\"],\"max\":4,\"max_patch_files\":100,\"max_patch_size\":4096,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"CONTRIBUTING.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\"],\"protected_files_policy\":\"request_review\",\"title_prefix\":\"[repo-assist] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_repo_memory\":{\"memories\":[{\"dir\":\"/tmp/gh-aw/repo-memory/default\",\"format_json\":true,\"id\":\"default\",\"max_file_count\":1,\"max_file_size\":65536,\"max_patch_size\":65536,\"validation\":{\"script\":\"const fail = message =\\u003e { throw new Error(`notes.json: ${message}`); };\\nconst data = JSON.parse(fs.readFileSync(path.join(memoryRoot, \\\"notes.json\\\"), \\\"utf8\\\"));\\nconst isObject = value =\\u003e value !== null \\u0026\\u0026 typeof value === \\\"object\\\" \\u0026\\u0026 !Array.isArray(value);\\nconst exactKeys = (value, keys) =\\u003e isObject(value) \\u0026\\u0026 Object.keys(value).sort().join(\\\",\\\") === [...keys].sort().join(\\\",\\\");\\nconst validDate = value =\\u003e typeof value === \\\"string\\\" \\u0026\\u0026 /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(value);\\nconst validText = (value, maximum) =\\u003e typeof value === \\\"string\\\" \\u0026\\u0026 value.length \\u003e 0 \\u0026\\u0026 value.length \\u003c= maximum;\\nconst unique = (entries, key, label) =\\u003e {\\n const values = entries.map(key);\\n if (new Set(values).size !== values.length) fail(`${label} must be unique`);\\n};\\n\\nif (!exactKeys(data, [\\\"version\\\", \\\"cursors\\\", \\\"issues\\\", \\\"fixes\\\", \\\"checks\\\", \\\"completed_actions\\\", \\\"priorities\\\"])) fail(\\\"must contain exactly version, cursors, issues, fixes, checks, completed_actions, and priorities\\\");\\nif (data.version !== 1) fail(\\\"version must be 1\\\");\\nif (!exactKeys(data.cursors, [\\\"labelling_after\\\", \\\"investigation_after\\\"])) fail(\\\"cursors must contain exactly labelling_after and investigation_after\\\");\\nfor (const [name, value] of Object.entries(data.cursors)) {\\n if (value !== null \\u0026\\u0026 (!Number.isInteger(value) || value \\u003c 1)) fail(`${name} must be null or a positive issue number`);\\n}\\n\\nconst issueStates = new Set([\\\"commented\\\", \\\"awaiting_clarification\\\", \\\"resolution_recommended\\\", \\\"deferred\\\", \\\"awaiting_approval\\\"]);\\nif (!Array.isArray(data.issues) || data.issues.length \\u003e 100) fail(\\\"issues must be an array of at most 100 entries\\\");\\nfor (const [index, entry] of data.issues.entries()) {\\n if (!exactKeys(entry, [\\\"number\\\", \\\"state\\\", \\\"updated_at\\\", \\\"note\\\"])) fail(`invalid issues entry at index ${index}`);\\n if (!Number.isInteger(entry.number) || entry.number \\u003c 1) fail(`invalid issue number at index ${index}`);\\n if (!issueStates.has(entry.state)) fail(`invalid issue state at index ${index}`);\\n if (!validDate(entry.updated_at)) fail(`invalid issue date at index ${index}`);\\n if (!validText(entry.note, 300)) fail(`invalid issue note at index ${index}`);\\n}\\nunique(data.issues, entry =\\u003e entry.number, \\\"issue numbers\\\");\\n\\nconst fixStates = new Set([\\\"open\\\", \\\"merged\\\", \\\"closed\\\", \\\"blocked\\\"]);\\nif (!Array.isArray(data.fixes) || data.fixes.length \\u003e 50) fail(\\\"fixes must be an array of at most 50 entries\\\");\\nfor (const [index, entry] of data.fixes.entries()) {\\n if (!exactKeys(entry, [\\\"issue\\\", \\\"pr\\\", \\\"branch\\\", \\\"status\\\", \\\"updated_at\\\", \\\"note\\\"])) fail(`invalid fixes entry at index ${index}`);\\n if (!Number.isInteger(entry.issue) || entry.issue \\u003c 1) fail(`invalid fix issue at index ${index}`);\\n if (entry.pr !== null \\u0026\\u0026 (!Number.isInteger(entry.pr) || entry.pr \\u003c 1)) fail(`invalid fix PR at index ${index}`);\\n if (entry.branch !== null \\u0026\\u0026 !validText(entry.branch, 120)) fail(`invalid fix branch at index ${index}`);\\n if (!fixStates.has(entry.status)) fail(`invalid fix status at index ${index}`);\\n if (!validDate(entry.updated_at)) fail(`invalid fix date at index ${index}`);\\n if (!validText(entry.note, 300)) fail(`invalid fix note at index ${index}`);\\n}\\nunique(data.fixes, entry =\\u003e entry.issue, \\\"fix issue numbers\\\");\\nunique(data.fixes.filter(entry =\\u003e entry.pr !== null), entry =\\u003e entry.pr, \\\"fix PR numbers\\\");\\n\\nconst checkAreas = new Set([\\\"dependencies\\\", \\\"ci\\\", \\\"tooling\\\", \\\"build\\\", \\\"code\\\", \\\"docs\\\", \\\"qa\\\", \\\"hygiene\\\", \\\"performance\\\", \\\"tests\\\", \\\"release\\\", \\\"repo_assist_prs\\\"]);\\nif (!Array.isArray(data.checks) || data.checks.length \\u003e checkAreas.size) fail(\\\"checks must contain at most one entry per area\\\");\\nfor (const [index, entry] of data.checks.entries()) {\\n if (!exactKeys(entry, [\\\"area\\\", \\\"checked_at\\\", \\\"result\\\", \\\"follow_up\\\"])) fail(`invalid checks entry at index ${index}`);\\n if (!checkAreas.has(entry.area)) fail(`invalid check area at index ${index}`);\\n if (!validDate(entry.checked_at)) fail(`invalid check date at index ${index}`);\\n if (!validText(entry.result, 300)) fail(`invalid check result at index ${index}`);\\n if (entry.follow_up !== null \\u0026\\u0026 !validText(entry.follow_up, 300)) fail(`invalid check follow_up at index ${index}`);\\n}\\nunique(data.checks, entry =\\u003e entry.area, \\\"check areas\\\");\\n\\nif (!Array.isArray(data.completed_actions) || data.completed_actions.length \\u003e 100) fail(\\\"completed_actions must be an array of at most 100 entries\\\");\\nfor (const [index, entry] of data.completed_actions.entries()) {\\n if (!exactKeys(entry, [\\\"key\\\", \\\"completed_at\\\"])) fail(`invalid completed_actions entry at index ${index}`);\\n if (!validText(entry.key, 100)) fail(`invalid completed action key at index ${index}`);\\n if (!validDate(entry.completed_at)) fail(`invalid completed action date at index ${index}`);\\n}\\nunique(data.completed_actions, entry =\\u003e entry.key, \\\"completed action keys\\\");\\n\\nif (!Array.isArray(data.priorities) || data.priorities.length \\u003e 20) fail(\\\"priorities must be an array of at most 20 entries\\\");\\nfor (const [index, entry] of data.priorities.entries()) {\\n if (!exactKeys(entry, [\\\"task\\\", \\\"item\\\", \\\"note\\\"])) fail(`invalid priorities entry at index ${index}`);\\n if (!Number.isInteger(entry.task) || entry.task \\u003c 1 || entry.task \\u003e 10) fail(`invalid priority task at index ${index}`);\\n if (!validText(entry.item, 100)) fail(`invalid priority item at index ${index}`);\\n if (!validText(entry.note, 300)) fail(`invalid priority note at index ${index}`);\\n}\\nunique(data.priorities, entry =\\u003e `${entry.task}:${entry.item}`, \\\"priority task/item pairs\\\");\\nconsole.log(\\\"repo-assist notes.json conforms to schema\\\");\\n\",\"timeout\":60}}]},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max\":4,\"max_patch_size\":4096,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"CONTRIBUTING.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\"],\"protected_files_policy\":\"allowed\",\"target\":\"*\",\"title_prefix\":\"[repo-assist] \"},\"remove_labels\":{\"allowed\":[\"bug\",\"enhancement\",\"help wanted\",\"good first issue\",\"spam\",\"off topic\",\"documentation\",\"question\",\"duplicate\",\"wontfix\",\"needs triage\",\"needs investigation\",\"breaking change\",\"performance\",\"security\",\"refactor\"],\"max\":5,\"target\":\"*\"},\"report_incomplete\":{},\"update_issue\":{\"allow_body\":true,\"max\":1,\"required_title_prefix\":\"[repo-assist] \",\"target\":\"*\"}}"
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": {
"add_comment": " CONSTRAINTS: Maximum 10 comment(s) can be added. Target: *. Supports reply_to_id for discussion threading.",
"add_labels": " CONSTRAINTS: Maximum 30 label(s) can be added. Only these labels are allowed: [\"bug\" \"enhancement\" \"help wanted\" \"good first issue\" \"spam\" \"off topic\" \"documentation\" \"question\" \"duplicate\" \"wontfix\" \"needs triage\" \"needs investigation\" \"breaking change\" \"performance\" \"security\" \"refactor\"]. Target: *.",
"create_issue": " CONSTRAINTS: Maximum 4 issue(s) can be created. Title will be prefixed with \"[repo-assist] \". Labels [\"automation\" \"repo-assist\"] will be automatically added.",
"create_pull_request": " CONSTRAINTS: Maximum 4 pull request(s) can be created. Title will be prefixed with \"[repo-assist] \". Labels [\"automation\" \"repo-assist\"] will be automatically added. PRs will be created as drafts.",
"push_to_pull_request_branch": " CONSTRAINTS: Maximum 4 push(es) can be made. The target pull request title must start with \"[repo-assist] \".",
"remove_labels": " CONSTRAINTS: Maximum 5 label(s) can be removed. Only these labels can be removed: [bug enhancement help wanted good first issue spam off topic documentation question duplicate wontfix needs triage needs investigation breaking change performance security refactor]. Target: *.",
"update_issue": " CONSTRAINTS: Maximum 1 issue(s) can be updated. Target: *. The target issue title must start with \"[repo-assist] \"."
},
"repo_params": {},
"dynamic_tools": []
}
GH_AW_VALIDATION_JSON: |
{
"add_comment": {
"defaultMax": 1,
"fields": {
"body": {
"required": true,
"type": "string",
"sanitize": true,
"maxLength": 65000
},
"comment_id": {
"optionalPositiveInteger": true
},
"item_number": {
"issueOrPRNumber": true
},
"pr": {
"issueOrPRNumber": true
},
"pr_number": {
"issueOrPRNumber": true
},
"reply_to_id": {
"type": "string",
"maxLength": 256
},
"repo": {
"type": "string",
"maxLength": 256
},
"target": {
"type": "string",
"enum": [
"status"
]
},
"temporary_id": {
"type": "string",
"pattern": "^#?aw_[A-Za-z0-9_]{3,12}$"
}
}
},
"add_labels": {
"defaultMax": 5,
"fields": {
"item_number": {
"issueNumberOrTemporaryId": true
},
"labels": {
"required": true,
"type": "array"
},
"repo": {
"type": "string",
"maxLength": 256
}
}
},
"create_issue": {
"defaultMax": 1,
"fields": {
"blocked_by": {},
"body": {
"required": true,
"type": "string",
"sanitize": true,
"maxLength": 65000,
"minLength": 20
},
"fields": {
"type": "array"
},
"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
}
}
},
"create_pull_request": {
"defaultMax": 1,
"fields": {
"base": {
"type": "string",
"sanitize": true,
"maxLength": 128
},
"body": {
"required": true,
"type": "string",
"sanitize": true,
"maxLength": 65000
},
"branch": {
"required": true,
"type": "string",
"sanitize": true,
"maxLength": 256
},
"dependencies": {
"type": "array",
"itemType": "string",
"itemSanitize": true,
"itemMaxLength": 256
},
"draft": {
"type": "boolean"
},
"labels": {
"type": "array",
"itemType": "string",
"itemSanitize": true,
"itemMaxLength": 128
},
"repo": {
"type": "string",
"maxLength": 256
},
"stack_position": {
"optionalPositiveInteger": true
},
"stack_root": {
"type": "string",
"sanitize": true,
"maxLength": 256
},
"temporary_id": {
"type": "string",
"pattern": "^#?aw_[A-Za-z0-9_]{3,12}$"
},
"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
}
}
},
"push_repo_memory": {
"defaultMax": 1,
"fields": {
"memory_id": {
"type": "string",
"sanitize": true,
"maxLength": 128
}
}
},
"push_to_pull_request_branch": {
"defaultMax": 1,
"fields": {
"branch": {
"type": "string",
"sanitize": true,
"maxLength": 256
},
"message": {
"required": true,
"type": "string",
"sanitize": true,
"maxLength": 65000
},
"pull_request_number": {
"issueOrPRNumber": true
},
"repo": {
"type": "string",
"maxLength": 256
}
}
},
"remove_labels": {
"defaultMax": 5,
"fields": {
"item_number": {
"issueNumberOrTemporaryId": true
},
"labels": {
"required": true,
"type": "array"
},
"repo": {
"type": "string",
"maxLength": 256
}
}
},
"report_incomplete": {
"defaultMax": 5,
"fields": {
"details": {
"type": "string",
"sanitize": true,
"maxLength": 65000
},
"reason": {
"required": true,
"type": "string",
"sanitize": true,
"maxLength": 1024
}
}
},
"update_issue": {
"defaultMax": 1,
"fields": {
"assignees": {
"type": "array",
"itemType": "string",
"itemSanitize": true,
"itemMaxLength": 39
},
"body": {
"type": "string",
"sanitize": true,
"maxLength": 65000
},
"issue_number": {
"issueOrPRNumber": true
},
"labels": {
"type": "array"
},
"milestone": {
"optionalPositiveInteger": true
},
"operation": {
"type": "string",
"enum": [
"replace",
"append",
"prepend",
"replace-island"
]
},
"repo": {
"type": "string",
"maxLength": 256
},
"status": {
"type": "string",
"enum": [
"open",
"closed"
]
},
"title": {
"type": "string",
"sanitize": true,
"maxLength": 128
}
},
"customValidation": "requiresOneOf:status,title,body,labels,assignees,milestone"
}
}
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 }}
GH_AW_SINK_VISIBILITY: ${{ steps.determine-automatic-lockdown.outputs.visibility }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -eo pipefail
mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config"
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_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 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"
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_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DOCKER_HOST=unix:///var/run/docker.sock -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_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.13'
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_cf44eb7d8be75a1a_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
{
"mcpServers": {
"github": {
"type": "stdio",
"container": "ghcr.io/github/github-mcp-server:v1.11.0",
"env": {
"GITHUB_FEATURES": "fields_param",
"GITHUB_HOST": "${GITHUB_SERVER_URL}",
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "all"
},
"guard-policies": {
"allow-only": {
"approval-labels": ${{ steps.parse-guard-vars.outputs.approval_labels }},
"blocked-users": ${{ steps.parse-guard-vars.outputs.blocked_users }},
"min-integrity": "none",
"repos": "all",
"trusted-users": ${{ steps.parse-guard-vars.outputs.trusted_users }}
}
}
},
"safeoutputs": {
"type": "stdio",
"container": "ghcr.io/github/gh-aw-node",
"mounts": ["\${GITHUB_WORKSPACE}:\${GITHUB_WORKSPACE}:rw", "${RUNNER_TEMP}/gh-aw/safeoutputs:${RUNNER_TEMP}/gh-aw/safeoutputs:rw", "/tmp/gh-aw:/tmp/gh-aw:rw"],
"args": ["-w", "\${GITHUB_WORKSPACE}"],
"entrypoint": "sh",
"entrypointArgs": ["-c", "sh ${RUNNER_TEMP}/gh-aw/safeoutputs/start_safe_outputs_mcp.sh"],
"env": {
"DEBUG": "*",
"DEFAULT_BRANCH": "\${DEFAULT_BRANCH}",
"GH_AW_ASSETS_ALLOWED_EXTS": "\${GH_AW_ASSETS_ALLOWED_EXTS}",
"GH_AW_ASSETS_BRANCH": "\${GH_AW_ASSETS_BRANCH}",
"GH_AW_ASSETS_MAX_SIZE_KB": "\${GH_AW_ASSETS_MAX_SIZE_KB}",
"GH_AW_MCP_LOG_DIR": "\${GH_AW_MCP_LOG_DIR}",
"GH_AW_SAFE_OUTPUTS": "\${GH_AW_SAFE_OUTPUTS}",
"GH_AW_SAFE_OUTPUTS_CONFIG_PATH": "\${GH_AW_SAFE_OUTPUTS_CONFIG_PATH}",
"GH_AW_SAFE_OUTPUTS_TOOLS_PATH": "\${GH_AW_SAFE_OUTPUTS_TOOLS_PATH}",
"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}"
},
"guard-policies": {
"write-sink": {
"accept": [
"*"
],
"sink-visibility": "${GH_AW_SINK_VISIBILITY}"
}
}
}
},
"gateway": {
"port": $MCP_GATEWAY_PORT,
"domain": "${MCP_GATEWAY_DOMAIN}",
"apiKey": "${MCP_GATEWAY_API_KEY}",
"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_cf44eb7d8be75a1a_EOF
- name: Mount MCP servers as CLIs
id: mount-mcp-clis
continue-on-error: true
env:
MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }}
MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const 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 github
# --allow-tool safeoutputs
# --allow-tool shell
# --allow-tool web_fetch
# --allow-tool write
timeout-minutes: 60
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)
GH_AW_MAX_AI_CREDITS="${GH_AW_MAX_AI_CREDITS:-1000}"
printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.28.9/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"*.githubusercontent.com\",\"*.gradle-enterprise.cloud\",\"*.pythonhosted.org\",\"*.vsblob.vsassets.io\",\"adoptium.net\",\"anaconda.org\",\"api.adoptium.net\",\"api.foojay.io\",\"api.npms.io\",\"api.nuget.org\",\"api.snapcraft.io\",\"appveyorcidata.blob.core.windows.net\",\"archive.apache.org\",\"archive.ubuntu.com\",\"azure.archive.ubuntu.com\",\"azuresearch-usnc.nuget.org\",\"azuresearch-ussc.nuget.org\",\"binstar.org\",\"bootstrap.pypa.io\",\"builds.dotnet.microsoft.com\",\"bun.sh\",\"cdn.azul.com\",\"cdn.jsdelivr.net\",\"central.sonatype.com\",\"ci.appveyor.com\",\"ci.dot.net\",\"codeload.github.com\",\"conda.anaconda.org\",\"conda.binstar.org\",\"crates.io\",\"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\",\"dc.services.visualstudio.com\",\"deb.nodesource.com\",\"deno.land\",\"develocity.apache.org\",\"dist.nuget.org\",\"dl.google.com\",\"dlcdn.apache.org\",\"docs.github.com\",\"dot.net\",\"dotnet.microsoft.com\",\"dotnetcli.blob.core.windows.net\",\"download.eclipse.org\",\"download.java.net\",\"download.oracle.com\",\"downloads.gradle-dn.com\",\"esm.sh\",\"files.pythonhosted.org\",\"ge.spockframework.org\",\"get.pnpm.io\",\"github-cloud.githubusercontent.com\",\"github-cloud.s3.amazonaws.com\",\"github.blog\",\"github.com\",\"github.githubassets.com\",\"googleapis.deno.dev\",\"googlechromelabs.github.io\",\"gradle.org\",\"index.crates.io\",\"jcenter.bintray.com\",\"jdk.java.net\",\"json-schema.org\",\"json.schemastore.org\",\"jsr.io\",\"keyserver.ubuntu.com\",\"lfs.github.com\",\"maven-central.storage-download.googleapis.com\",\"maven.apache.org\",\"maven.google.com\",\"maven.oracle.com\",\"maven.pkg.github.com\",\"nodejs.org\",\"npm.pkg.github.com\",\"npmjs.com\",\"npmjs.org\",\"nuget.org\",\"nuget.pkg.github.com\",\"nugetregistryv2prod.blob.core.windows.net\",\"objects.githubusercontent.com\",\"ocsp.digicert.com\",\"ocsp.geotrust.com\",\"ocsp.globalsign.com\",\"ocsp.identrust.com\",\"ocsp.sectigo.com\",\"ocsp.ssl.com\",\"ocsp.thawte.com\",\"ocsp.usertrust.com\",\"ocsp.verisign.com\",\"oneocsp.microsoft.com\",\"packagecloud.io\",\"packages.cloud.google.com\",\"packages.microsoft.com\",\"patch-diff.githubusercontent.com\",\"patchdiff.githubusercontent.com\",\"pip.pypa.io\",\"pkgs.dev.azure.com\",\"plugins-artifacts.gradle.org\",\"plugins.gradle.org\",\"ppa.launchpad.net\",\"pypi.org\",\"pypi.python.org\",\"raw.githubusercontent.com\",\"registry.bower.io\",\"registry.npmjs.com\",\"registry.npmjs.org\",\"registry.yarnpkg.com\",\"repo.anaconda.com\",\"repo.continuum.io\",\"repo.gradle.org\",\"repo.grails.org\",\"repo.maven.apache.org\",\"repo.spring.io\",\"repo.yarnpkg.com\",\"repo1.maven.org\",\"repository.apache.org\",\"s.symcb.com\",\"s.symcd.com\",\"scans-in.gradle.com\",\"security.ubuntu.com\",\"services.gradle.org\",\"sh.rustup.rs\",\"skimdb.npmjs.com\",\"static.crates.io\",\"static.rust-lang.org\",\"storage.googleapis.com\",\"telemetry.vercel.com\",\"ts-crl.ws.symantec.com\",\"ts-ocsp.ws.symantec.com\",\"www.googleapis.com\",\"www.java.com\",\"www.microsoft.com\",\"www.npmjs.com\",\"www.npmjs.org\",\"yarnpkg.com\"],\"isolation\":true,\"topologyAttach\":[\"awmg-mcpg\"]},\"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.9,squid=sha256:3d5dba0b0a139bbb11b5d5b8b44f277d2b18f69cf43090e3c283d750cf864baa,agent=sha256:54b2fb3068efc15a4cc1bd4033f8fa056a9b1779baeba0cb80ae95ea55e7e343,api-proxy=sha256:a0ffb1dc926c6e5a500b336893e032a8f167d3db43c869be886874ef14280bb8,cli-proxy=sha256:38d7ac0585ee5aa6a06eb71e087d514b059db36005c7783c6485e0dfd36fea35\"},\"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 GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --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/ --no-ask-user --allow-tool github --allow-tool safeoutputs --allow-tool shell --allow-tool web_fetch --allow-tool write --allow-all-paths --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: ${{ secrets.COPILOT_GITHUB_TOKEN }}
COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || vars.GH_AW_DEFAULT_MODEL_COPILOT || 'auto' }}
GH_AW_LLM_PROVIDER: github
GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }}
GH_AW_MAX_TURNS: ${{ vars.GH_AW_DEFAULT_MAX_TURNS || '' }}
GH_AW_PHASE: agent
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
GH_AW_TIMEOUT_MINUTES: 60
GH_AW_VERSION: dev
GITHUB_API_URL: ${{ github.api_url }}
GITHUB_AW: true
GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
GITHUB_HEAD_REF: ${{ github.head_ref }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_REF_NAME: ${{ github.ref_name }}
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
GITHUB_WORKSPACE: ${{ github.workspace }}
GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
GIT_AUTHOR_NAME: github-actions[bot]
GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
GIT_COMMITTER_NAME: github-actions[bot]
RUNNER_TEMP: ${{ runner.temp }}
TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }}
- name: Detect agent errors
if: always()
id: detect-agent-errors
continue-on-error: true
env:
GH_AW_AGENTIC_EXECUTION_OUTCOME: ${{ steps.agentic_execution.outcome }}
GH_AW_ENGINE_STEP_TIMEOUT_MINUTES: 60
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_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }}
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const 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: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN'
SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh"
- name: Copy Safe Outputs
if: always()
env:
GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
run: |
mkdir -p /tmp/gh-aw
cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true
- name: Ingest agent output
id: collect_output
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.gradle-enterprise.cloud,*.pythonhosted.org,*.vsblob.vsassets.io,adoptium.net,anaconda.org,api.adoptium.net,api.foojay.io,api.npms.io,api.nuget.org,api.snapcraft.io,appveyorcidata.blob.core.windows.net,archive.apache.org,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,binstar.org,bootstrap.pypa.io,builds.dotnet.microsoft.com,bun.sh,cdn.azul.com,cdn.jsdelivr.net,central.sonatype.com,ci.appveyor.com,ci.dot.net,codeload.github.com,conda.anaconda.org,conda.binstar.org,crates.io,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,dc.services.visualstudio.com,deb.nodesource.com,deno.land,develocity.apache.org,dist.nuget.org,dl.google.com,dlcdn.apache.org,docs.github.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,download.eclipse.org,download.java.net,download.oracle.com,downloads.gradle-dn.com,esm.sh,files.pythonhosted.org,ge.spockframework.org,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,googleapis.deno.dev,googlechromelabs.github.io,gradle.org,index.crates.io,jcenter.bintray.com,jdk.java.net,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,maven-central.storage-download.googleapis.com,maven.apache.org,maven.google.com,maven.oracle.com,maven.pkg.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,patchdiff.githubusercontent.com,pip.pypa.io,pkgs.dev.azure.com,plugins-artifacts.gradle.org,plugins.gradle.org,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.gradle.org,repo.grails.org,repo.maven.apache.org,repo.spring.io,repo.yarnpkg.com,repo1.maven.org,repository.apache.org,s.symcb.com,s.symcd.com,scans-in.gradle.com,security.ubuntu.com,services.gradle.org,sh.rustup.rs,skimdb.npmjs.com,static.crates.io,static.rust-lang.org,storage.googleapis.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.java.com,www.microsoft.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_COMMANDS: "[\"repo-assist\"]"
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
# Upload repo memory as artifacts for push job
- name: Sanitize repo-memory filenames (default)
if: always()
continue-on-error: true
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
run: bash "${RUNNER_TEMP}/gh-aw/actions/sanitize_repo_memory_filenames.sh"
- name: Validate repo-memory domain content (default)
id: validate_repo_memory_64656661756c74
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
MEMORY_DIR: /tmp/gh-aw/repo-memory/default
MEMORY_ID: default
VALIDATION_SCRIPT_B64: Y29uc3QgZmFpbCA9IG1lc3NhZ2UgPT4geyB0aHJvdyBuZXcgRXJyb3IoYG5vdGVzLmpzb246ICR7bWVzc2FnZX1gKTsgfTsKY29uc3QgZGF0YSA9IEpTT04ucGFyc2UoZnMucmVhZEZpbGVTeW5jKHBhdGguam9pbihtZW1vcnlSb290LCAibm90ZXMuanNvbiIpLCAidXRmOCIpKTsKY29uc3QgaXNPYmplY3QgPSB2YWx1ZSA9PiB2YWx1ZSAhPT0gbnVsbCAmJiB0eXBlb2YgdmFsdWUgPT09ICJvYmplY3QiICYmICFBcnJheS5pc0FycmF5KHZhbHVlKTsKY29uc3QgZXhhY3RLZXlzID0gKHZhbHVlLCBrZXlzKSA9PiBpc09iamVjdCh2YWx1ZSkgJiYgT2JqZWN0LmtleXModmFsdWUpLnNvcnQoKS5qb2luKCIsIikgPT09IFsuLi5rZXlzXS5zb3J0KCkuam9pbigiLCIpOwpjb25zdCB2YWxpZERhdGUgPSB2YWx1ZSA9PiB0eXBlb2YgdmFsdWUgPT09ICJzdHJpbmciICYmIC9eWzAtOV17NH0tWzAtOV17Mn0tWzAtOV17Mn0kLy50ZXN0KHZhbHVlKTsKY29uc3QgdmFsaWRUZXh0ID0gKHZhbHVlLCBtYXhpbXVtKSA9PiB0eXBlb2YgdmFsdWUgPT09ICJzdHJpbmciICYmIHZhbHVlLmxlbmd0aCA+IDAgJiYgdmFsdWUubGVuZ3RoIDw9IG1heGltdW07CmNvbnN0IHVuaXF1ZSA9IChlbnRyaWVzLCBrZXksIGxhYmVsKSA9PiB7CiAgY29uc3QgdmFsdWVzID0gZW50cmllcy5tYXAoa2V5KTsKICBpZiAobmV3IFNldCh2YWx1ZXMpLnNpemUgIT09IHZhbHVlcy5sZW5ndGgpIGZhaWwoYCR7bGFiZWx9IG11c3QgYmUgdW5pcXVlYCk7Cn07CgppZiAoIWV4YWN0S2V5cyhkYXRhLCBbInZlcnNpb24iLCAiY3Vyc29ycyIsICJpc3N1ZXMiLCAiZml4ZXMiLCAiY2hlY2tzIiwgImNvbXBsZXRlZF9hY3Rpb25zIiwgInByaW9yaXRpZXMiXSkpIGZhaWwoIm11c3QgY29udGFpbiBleGFjdGx5IHZlcnNpb24sIGN1cnNvcnMsIGlzc3VlcywgZml4ZXMsIGNoZWNrcywgY29tcGxldGVkX2FjdGlvbnMsIGFuZCBwcmlvcml0aWVzIik7CmlmIChkYXRhLnZlcnNpb24gIT09IDEpIGZhaWwoInZlcnNpb24gbXVzdCBiZSAxIik7CmlmICghZXhhY3RLZXlzKGRhdGEuY3Vyc29ycywgWyJsYWJlbGxpbmdfYWZ0ZXIiLCAiaW52ZXN0aWdhdGlvbl9hZnRlciJdKSkgZmFpbCgiY3Vyc29ycyBtdXN0IGNvbnRhaW4gZXhhY3RseSBsYWJlbGxpbmdfYWZ0ZXIgYW5kIGludmVzdGlnYXRpb25fYWZ0ZXIiKTsKZm9yIChjb25zdCBbbmFtZSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKGRhdGEuY3Vyc29ycykpIHsKICBpZiAodmFsdWUgIT09IG51bGwgJiYgKCFOdW1iZXIuaXNJbnRlZ2VyKHZhbHVlKSB8fCB2YWx1ZSA8IDEpKSBmYWlsKGAke25hbWV9IG11c3QgYmUgbnVsbCBvciBhIHBvc2l0aXZlIGlzc3VlIG51bWJlcmApOwp9Cgpjb25zdCBpc3N1ZVN0YXRlcyA9IG5ldyBTZXQoWyJjb21tZW50ZWQiLCAiYXdhaXRpbmdfY2xhcmlmaWNhdGlvbiIsICJyZXNvbHV0aW9uX3JlY29tbWVuZGVkIiwgImRlZmVycmVkIiwgImF3YWl0aW5nX2FwcHJvdmFsIl0pOwppZiAoIUFycmF5LmlzQXJyYXkoZGF0YS5pc3N1ZXMpIHx8IGRhdGEuaXNzdWVzLmxlbmd0aCA+IDEwMCkgZmFpbCgiaXNzdWVzIG11c3QgYmUgYW4gYXJyYXkgb2YgYXQgbW9zdCAxMDAgZW50cmllcyIpOwpmb3IgKGNvbnN0IFtpbmRleCwgZW50cnldIG9mIGRhdGEuaXNzdWVzLmVudHJpZXMoKSkgewogIGlmICghZXhhY3RLZXlzKGVudHJ5LCBbIm51bWJlciIsICJzdGF0ZSIsICJ1cGRhdGVkX2F0IiwgIm5vdGUiXSkpIGZhaWwoYGludmFsaWQgaXNzdWVzIGVudHJ5IGF0IGluZGV4ICR7aW5kZXh9YCk7CiAgaWYgKCFOdW1iZXIuaXNJbnRlZ2VyKGVudHJ5Lm51bWJlcikgfHwgZW50cnkubnVtYmVyIDwgMSkgZmFpbChgaW52YWxpZCBpc3N1ZSBudW1iZXIgYXQgaW5kZXggJHtpbmRleH1gKTsKICBpZiAoIWlzc3VlU3RhdGVzLmhhcyhlbnRyeS5zdGF0ZSkpIGZhaWwoYGludmFsaWQgaXNzdWUgc3RhdGUgYXQgaW5kZXggJHtpbmRleH1gKTsKICBpZiAoIXZhbGlkRGF0ZShlbnRyeS51cGRhdGVkX2F0KSkgZmFpbChgaW52YWxpZCBpc3N1ZSBkYXRlIGF0IGluZGV4ICR7aW5kZXh9YCk7CiAgaWYgKCF2YWxpZFRleHQoZW50cnkubm90ZSwgMzAwKSkgZmFpbChgaW52YWxpZCBpc3N1ZSBub3RlIGF0IGluZGV4ICR7aW5kZXh9YCk7Cn0KdW5pcXVlKGRhdGEuaXNzdWVzLCBlbnRyeSA9PiBlbnRyeS5udW1iZXIsICJpc3N1ZSBudW1iZXJzIik7Cgpjb25zdCBmaXhTdGF0ZXMgPSBuZXcgU2V0KFsib3BlbiIsICJtZXJnZWQiLCAiY2xvc2VkIiwgImJsb2NrZWQiXSk7CmlmICghQXJyYXkuaXNBcnJheShkYXRhLmZpeGVzKSB8fCBkYXRhLmZpeGVzLmxlbmd0aCA+IDUwKSBmYWlsKCJmaXhlcyBtdXN0IGJlIGFuIGFycmF5IG9mIGF0IG1vc3QgNTAgZW50cmllcyIpOwpmb3IgKGNvbnN0IFtpbmRleCwgZW50cnldIG9mIGRhdGEuZml4ZXMuZW50cmllcygpKSB7CiAgaWYgKCFleGFjdEtleXMoZW50cnksIFsiaXNzdWUiLCAicHIiLCAiYnJhbmNoIiwgInN0YXR1cyIsICJ1cGRhdGVkX2F0IiwgIm5vdGUiXSkpIGZhaWwoYGludmFsaWQgZml4ZXMgZW50cnkgYXQgaW5kZXggJHtpbmRleH1gKTsKICBpZiAoIU51bWJlci5pc0ludGVnZXIoZW50cnkuaXNzdWUpIHx8IGVudHJ5Lmlzc3VlIDwgMSkgZmFpbChgaW52YWxpZCBmaXggaXNzdWUgYXQgaW5kZXggJHtpbmRleH1gKTsKICBpZiAoZW50cnkucHIgIT09IG51bGwgJiYgKCFOdW1iZXIuaXNJbnRlZ2VyKGVudHJ5LnByKSB8fCBlbnRyeS5wciA8IDEpKSBmYWlsKGBpbnZhbGlkIGZpeCBQUiBhdCBpbmRleCAke2luZGV4fWApOwogIGlmIChlbnRyeS5icmFuY2ggIT09IG51bGwgJiYgIXZhbGlkVGV4dChlbnRyeS5icmFuY2gsIDEyMCkpIGZhaWwoYGludmFsaWQgZml4IGJyYW5jaCBhdCBpbmRleCAke2luZGV4fWApOwogIGlmICghZml4U3RhdGVzLmhhcyhlbnRyeS5zdGF0dXMpKSBmYWlsKGBpbnZhbGlkIGZpeCBzdGF0dXMgYXQgaW5kZXggJHtpbmRleH1gKTsKICBpZiAoIXZhbGlkRGF0ZShlbnRyeS51cGRhdGVkX2F0KSkgZmFpbChgaW52YWxpZCBmaXggZGF0ZSBhdCBpbmRleCAke2luZGV4fWApOwogIGlmICghdmFsaWRUZXh0KGVudHJ5Lm5vdGUsIDMwMCkpIGZhaWwoYGludmFsaWQgZml4IG5vdGUgYXQgaW5kZXggJHtpbmRleH1gKTsKfQp1bmlxdWUoZGF0YS5maXhlcywgZW50cnkgPT4gZW50cnkuaXNzdWUsICJmaXggaXNzdWUgbnVtYmVycyIpOwp1bmlxdWUoZGF0YS5maXhlcy5maWx0ZXIoZW50cnkgPT4gZW50cnkucHIgIT09IG51bGwpLCBlbnRyeSA9PiBlbnRyeS5wciwgImZpeCBQUiBudW1iZXJzIik7Cgpjb25zdCBjaGVja0FyZWFzID0gbmV3IFNldChbImRlcGVuZGVuY2llcyIsICJjaSIsICJ0b29saW5nIiwgImJ1aWxkIiwgImNvZGUiLCAiZG9jcyIsICJxYSIsICJoeWdpZW5lIiwgInBlcmZvcm1hbmNlIiwgInRlc3RzIiwgInJlbGVhc2UiLCAicmVwb19hc3Npc3RfcHJzIl0pOwppZiAoIUFycmF5LmlzQXJyYXkoZGF0YS5jaGVja3MpIHx8IGRhdGEuY2hlY2tzLmxlbmd0aCA+IGNoZWNrQXJlYXMuc2l6ZSkgZmFpbCgiY2hlY2tzIG11c3QgY29udGFpbiBhdCBtb3N0IG9uZSBlbnRyeSBwZXIgYXJlYSIpOwpmb3IgKGNvbnN0IFtpbmRleCwgZW50cnldIG9mIGRhdGEuY2hlY2tzLmVudHJpZXMoKSkgewogIGlmICghZXhhY3RLZXlzKGVudHJ5LCBbImFyZWEiLCAiY2hlY2tlZF9hdCIsICJyZXN1bHQiLCAiZm9sbG93X3VwIl0pKSBmYWlsKGBpbnZhbGlkIGNoZWNrcyBlbnRyeSBhdCBpbmRleCAke2luZGV4fWApOwogIGlmICghY2hlY2tBcmVhcy5oYXMoZW50cnkuYXJlYSkpIGZhaWwoYGludmFsaWQgY2hlY2sgYXJlYSBhdCBpbmRleCAke2luZGV4fWApOwogIGlmICghdmFsaWREYXRlKGVudHJ5LmNoZWNrZWRfYXQpKSBmYWlsKGBpbnZhbGlkIGNoZWNrIGRhdGUgYXQgaW5kZXggJHtpbmRleH1gKTsKICBpZiAoIXZhbGlkVGV4dChlbnRyeS5yZXN1bHQsIDMwMCkpIGZhaWwoYGludmFsaWQgY2hlY2sgcmVzdWx0IGF0IGluZGV4ICR7aW5kZXh9YCk7CiAgaWYgKGVudHJ5LmZvbGxvd191cCAhPT0gbnVsbCAmJiAhdmFsaWRUZXh0KGVudHJ5LmZvbGxvd191cCwgMzAwKSkgZmFpbChgaW52YWxpZCBjaGVjayBmb2xsb3dfdXAgYXQgaW5kZXggJHtpbmRleH1gKTsKfQp1bmlxdWUoZGF0YS5jaGVja3MsIGVudHJ5ID0+IGVudHJ5LmFyZWEsICJjaGVjayBhcmVhcyIpOwoKaWYgKCFBcnJheS5pc0FycmF5KGRhdGEuY29tcGxldGVkX2FjdGlvbnMpIHx8IGRhdGEuY29tcGxldGVkX2FjdGlvbnMubGVuZ3RoID4gMTAwKSBmYWlsKCJjb21wbGV0ZWRfYWN0aW9ucyBtdXN0IGJlIGFuIGFycmF5IG9mIGF0IG1vc3QgMTAwIGVudHJpZXMiKTsKZm9yIChjb25zdCBbaW5kZXgsIGVudHJ5XSBvZiBkYXRhLmNvbXBsZXRlZF9hY3Rpb25zLmVudHJpZXMoKSkgewogIGlmICghZXhhY3RLZXlzKGVudHJ5LCBbImtleSIsICJjb21wbGV0ZWRfYXQiXSkpIGZhaWwoYGludmFsaWQgY29tcGxldGVkX2FjdGlvbnMgZW50cnkgYXQgaW5kZXggJHtpbmRleH1gKTsKICBpZiAoIXZhbGlkVGV4dChlbnRyeS5rZXksIDEwMCkpIGZhaWwoYGludmFsaWQgY29tcGxldGVkIGFjdGlvbiBrZXkgYXQgaW5kZXggJHtpbmRleH1gKTsKICBpZiAoIXZhbGlkRGF0ZShlbnRyeS5jb21wbGV0ZWRfYXQpKSBmYWlsKGBpbnZhbGlkIGNvbXBsZXRlZCBhY3Rpb24gZGF0ZSBhdCBpbmRleCAke2luZGV4fWApOwp9CnVuaXF1ZShkYXRhLmNvbXBsZXRlZF9hY3Rpb25zLCBlbnRyeSA9PiBlbnRyeS5rZXksICJjb21wbGV0ZWQgYWN0aW9uIGtleXMiKTsKCmlmICghQXJyYXkuaXNBcnJheShkYXRhLnByaW9yaXRpZXMpIHx8IGRhdGEucHJpb3JpdGllcy5sZW5ndGggPiAyMCkgZmFpbCgicHJpb3JpdGllcyBtdXN0IGJlIGFuIGFycmF5IG9mIGF0IG1vc3QgMjAgZW50cmllcyIpOwpmb3IgKGNvbnN0IFtpbmRleCwgZW50cnldIG9mIGRhdGEucHJpb3JpdGllcy5lbnRyaWVzKCkpIHsKICBpZiAoIWV4YWN0S2V5cyhlbnRyeSwgWyJ0YXNrIiwgIml0ZW0iLCAibm90ZSJdKSkgZmFpbChgaW52YWxpZCBwcmlvcml0aWVzIGVudHJ5IGF0IGluZGV4ICR7aW5kZXh9YCk7CiAgaWYgKCFOdW1iZXIuaXNJbnRlZ2VyKGVudHJ5LnRhc2spIHx8IGVudHJ5LnRhc2sgPCAxIHx8IGVudHJ5LnRhc2sgPiAxMCkgZmFpbChgaW52YWxpZCBwcmlvcml0eSB0YXNrIGF0IGluZGV4ICR7aW5kZXh9YCk7CiAgaWYgKCF2YWxpZFRleHQoZW50cnkuaXRlbSwgMTAwKSkgZmFpbChgaW52YWxpZCBwcmlvcml0eSBpdGVtIGF0IGluZGV4ICR7aW5kZXh9YCk7CiAgaWYgKCF2YWxpZFRleHQoZW50cnkubm90ZSwgMzAwKSkgZmFpbChgaW52YWxpZCBwcmlvcml0eSBub3RlIGF0IGluZGV4ICR7aW5kZXh9YCk7Cn0KdW5pcXVlKGRhdGEucHJpb3JpdGllcywgZW50cnkgPT4gYCR7ZW50cnkudGFza306JHtlbnRyeS5pdGVtfWAsICJwcmlvcml0eSB0YXNrL2l0ZW0gcGFpcnMiKTsKY29uc29sZS5sb2coInJlcG8tYXNzaXN0IG5vdGVzLmpzb24gY29uZm9ybXMgdG8gc2NoZW1hIik7Cg==
VALIDATION_TIMEOUT_SECONDS: 60
FORMAT_JSON: '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 { validateMemoryStep } = require(path.join(actionsDir, 'validate_memory_step.cjs'));
validateMemoryStep(core, { kind: 'repo', formatJSON: process.env.FORMAT_JSON === 'true', requireValidationScript: true });
- name: Upload repo-memory artifact (default)
if: always() && steps.validate_repo_memory_64656661756c74.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: repo-memory-default
path: /tmp/gh-aw/repo-memory/default
retention-days: 1
if-no-files-found: ignore
# 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/proxy-logs/
!/tmp/gh-aw/proxy-logs/proxy-tls/
/tmp/gh-aw/agent_usage.json
/tmp/gh-aw/agent-stdio.log
/tmp/gh-aw/pre-agent-audit.txt
/tmp/gh-aw/agent/
/tmp/gh-aw/github_rate_limits.jsonl
/tmp/gh-aw/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
- push_repo_memory
- 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.secret_verification_result == 'failed' || needs.activation.outputs.daily_ai_credits_exceeded == 'true')
runs-on: ubuntu-slim
permissions:
actions: read
contents: write
issues: write
pull-requests: write
concurrency:
group: "gh-aw-conclusion-repo-assist"
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@76f6ea7c222034dcb6282848ba08935d75df1b8f # 76f6ea7c222034dcb6282848ba08935d75df1b8f
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: "Repo Assist"
GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/repo-assist.lock.yml@${{ github.ref }}
GH_AW_INFO_VERSION: "1.0.80"
GH_AW_INFO_AWF_VERSION: "v0.28.9"
GH_AW_INFO_BODY_MODIFIED: "false"
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-repoassist-${{ github.run_id }}
restore-keys: agentic-workflow-usage-repoassist-
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-repoassist-${{ 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: "Repo Assist"
GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/repo-assist.md@ae8d551f07c7ed7619f8c58c7bb4c3ac89395d38"
GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/ae8d551f07c7ed7619f8c58c7bb4c3ac89395d38/workflows/repo-assist.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: "repo-assist"
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: "Repo Assist"
GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/repo-assist.md@ae8d551f07c7ed7619f8c58c7bb4c3ac89395d38"
GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/ae8d551f07c7ed7619f8c58c7bb4c3ac89395d38/workflows/repo-assist.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: "Repo Assist"
GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/repo-assist.md@ae8d551f07c7ed7619f8c58c7bb4c3ac89395d38"
GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/ae8d551f07c7ed7619f8c58c7bb4c3ac89395d38/workflows/repo-assist.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: "true"
GH_AW_WORKFLOW_NAME: "Repo Assist"
GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/repo-assist.md@ae8d551f07c7ed7619f8c58c7bb4c3ac89395d38"
GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/ae8d551f07c7ed7619f8c58c7bb4c3ac89395d38/workflows/repo-assist.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: "Repo Assist"
GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/repo-assist.md@ae8d551f07c7ed7619f8c58c7bb4c3ac89395d38"
GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/ae8d551f07c7ed7619f8c58c7bb4c3ac89395d38/workflows/repo-assist.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: "repo-assist"
GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "0"
GH_AW_ENGINE_ID: "copilot"
GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }}
GH_AW_ENGINE_SECRET_FAILURE_MESSAGE: "**Alternative**: If your organization has a Copilot subscription, you can avoid the need for a personal access token by adding a top-level `permissions` block to your workflow file. This enables Copilot inference through the org using the built-in GitHub Actions token.\n\n```yaml\npermissions:\n copilot-requests: write\n```\n\nSee: https://github.github.com/gh-aw/reference/engines/#github-copilot-default"
GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }}
GH_AW_AI_CREDITS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.ai_credits_rate_limit_error || 'false' }}
GH_AW_UNKNOWN_MODEL_AI_CREDITS: ${{ needs.agent.outputs.unknown_model_ai_credits || 'false' }}
GH_AW_AIC: ${{ needs.agent.outputs.aic }}
GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }}
GH_AW_MAX_AI_CREDITS: ${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }}
GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }}
GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }}
GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }}
GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }}
GH_AW_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_CODE_PUSH_FAILURE_ERRORS: ${{ needs.safe_outputs.outputs.code_push_failure_errors }}
GH_AW_CODE_PUSH_FAILURE_COUNT: ${{ needs.safe_outputs.outputs.code_push_failure_count }}
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_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e Generated by 🌈 {workflow_name}, see [workflow run]({run_url}). [Learn more](https://github.com/githubnext/agentics/blob/main/docs/repo-assist.md).\",\"runStarted\":\"{workflow_name} is processing {event_type}, see [workflow run]({run_url})...\",\"runSuccess\":\"✓ {workflow_name} completed successfully, see [workflow run]({run_url}).\",\"runFailure\":\"✗ {workflow_name} encountered {status}, see [workflow run]({run_url}).\"}"
GH_AW_PUSH_REPO_MEMORY_RESULT: ${{ needs.push_repo_memory.result }}
GH_AW_REPO_MEMORY_VALIDATION_FAILED_default: ${{ needs.push_repo_memory.outputs.validation_failed_default }}
GH_AW_REPO_MEMORY_VALIDATION_ERROR_default: ${{ needs.push_repo_memory.outputs.validation_error_default }}
GH_AW_REPO_MEMORY_PATCH_SIZE_EXCEEDED_default: ${{ needs.push_repo_memory.outputs.patch_size_exceeded_default }}
GH_AW_GROUP_REPORTS: "false"
GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true"
GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true"
GH_AW_TIMEOUT_MINUTES: "60"
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();
- name: Report failed jobs
id: report_failed_jobs
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: "Repo Assist"
GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/repo-assist.md@ae8d551f07c7ed7619f8c58c7bb4c3ac89395d38"
GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/ae8d551f07c7ed7619f8c58c7bb4c3ac89395d38/workflows/repo-assist.md"
GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
GH_AW_REPORT_FAILED_JOBS: "true"
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_failed_jobs.cjs'));
await main();
- name: Update reaction comment with completion status
id: conclusion
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_COMMENT_REPO: ${{ needs.activation.outputs.comment_repo }}
GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
GH_AW_WORKFLOW_NAME: "Repo Assist"
GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
GH_AW_SAFE_OUTPUTS_RESULT: ${{ needs.safe_outputs.result }}
GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e Generated by 🌈 {workflow_name}, see [workflow run]({run_url}). [Learn more](https://github.com/githubnext/agentics/blob/main/docs/repo-assist.md).\",\"runStarted\":\"{workflow_name} is processing {event_type}, see [workflow run]({run_url})...\",\"runSuccess\":\"✓ {workflow_name} completed successfully, see [workflow run]({run_url}).\",\"runFailure\":\"✗ {workflow_name} encountered {status}, see [workflow run]({run_url}).\"}"
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, 'notify_comment_error.cjs'));
await main();
detection:
needs:
- activation
- agent
if: always() && needs.agent.result != 'skipped'
runs-on: ubuntu-latest
permissions:
contents: read
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@76f6ea7c222034dcb6282848ba08935d75df1b8f # 76f6ea7c222034dcb6282848ba08935d75df1b8f
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: "Repo Assist"
GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/repo-assist.lock.yml@${{ github.ref }}
GH_AW_INFO_VERSION: "1.0.80"
GH_AW_INFO_AWF_VERSION: "v0.28.9"
GH_AW_INFO_BODY_MODIFIED: "false"
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.9@sha256:54b2fb3068efc15a4cc1bd4033f8fa056a9b1779baeba0cb80ae95ea55e7e343 ghcr.io/github/gh-aw-firewall/api-proxy:0.28.9@sha256:a0ffb1dc926c6e5a500b336893e032a8f167d3db43c869be886874ef14280bb8 ghcr.io/github/gh-aw-firewall/squid:0.28.9@sha256:3d5dba0b0a139bbb11b5d5b8b44f277d2b18f69cf43090e3c283d750cf864baa
- 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: "Repo Assist"
WORKFLOW_DESCRIPTION: "A friendly repository assistant that runs regularly (twice a day by default) to assist maintainers.\nCan also be triggered on-demand via '/repo-assist <instructions>' to perform specific tasks.\n- Labels and triages open issues\n- Investigates open issues and resolves, fixes, clarifies, or comments on them\n- Identifies issues that can be fixed and creates draft pull requests with fixes\n- Improves performance, testing, and code quality via PRs\n- Makes engineering investments: dependency updates, CI improvements, tooling\n- Updates its own PRs when CI fails or merge conflicts arise\n- Improves documentation, performs ad hoc QA, and maintains project basics\n- Takes the repository forward with proactive improvements\n- Maintains a persistent memory of work done and what remains\nAlways polite, constructive, and mindful of the project's goals."
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.9 --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: dev
- 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: ${{ secrets.COPILOT_GITHUB_TOKEN }}
COPILOT_MODEL: detection
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: dev
GITHUB_API_URL: ${{ github.api_url }}
GITHUB_AW: true
GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
GITHUB_HEAD_REF: ${{ github.head_ref }}
GITHUB_REF_NAME: ${{ github.ref_name }}
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
GITHUB_WORKSPACE: ${{ github.workspace }}
GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
GIT_AUTHOR_NAME: github-actions[bot]
GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
GIT_COMMITTER_NAME: github-actions[bot]
RUNNER_TEMP: ${{ runner.temp }}
TRACEPARENT: ${{ env.GITHUB_AW_OTEL_TRACE_ID != '' && env.GITHUB_AW_OTEL_PARENT_SPAN_ID != '' && format('00-{0}-{1}-01', env.GITHUB_AW_OTEL_TRACE_ID, env.GITHUB_AW_OTEL_PARENT_SPAN_ID) || '' }}
WORKFLOW_NAME: "Repo Assist"
WORKFLOW_DESCRIPTION: "A friendly repository assistant that runs regularly (twice a day by default) to assist maintainers.\nCan also be triggered on-demand via '/repo-assist <instructions>' to perform specific tasks.\n- Labels and triages open issues\n- Investigates open issues and resolves, fixes, clarifies, or comments on them\n- Identifies issues that can be fixed and creates draft pull requests with fixes\n- Improves performance, testing, and code quality via PRs\n- Makes engineering investments: dependency updates, CI improvements, tooling\n- Updates its own PRs when CI fails or merge conflicts arise\n- Improves documentation, performs ad hoc QA, and maintains project basics\n- Takes the repository forward with proactive improvements\n- Maintains a persistent memory of work done and what remains\nAlways polite, constructive, and mindful of the project's goals."
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.9/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.9,squid=sha256:3d5dba0b0a139bbb11b5d5b8b44f277d2b18f69cf43090e3c283d750cf864baa,agent=sha256:54b2fb3068efc15a4cc1bd4033f8fa056a9b1779baeba0cb80ae95ea55e7e343,api-proxy=sha256:a0ffb1dc926c6e5a500b336893e032a8f167d3db43c869be886874ef14280bb8,cli-proxy=sha256:38d7ac0585ee5aa6a06eb71e087d514b059db36005c7783c6485e0dfd36fea35\"},\"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:
pull-requests: read
env:
GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
outputs:
activated: ${{ steps.check_membership.outputs.is_team_member == 'true' && steps.check_command_position.outputs.command_position_ok == 'true' }}
check_result: ${{ steps.check.outcome }}
matched_command: ${{ steps.check_command_position.outputs.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@76f6ea7c222034dcb6282848ba08935d75df1b8f # 76f6ea7c222034dcb6282848ba08935d75df1b8f
with:
destination: ${{ runner.temp }}/gh-aw/actions
job-name: ${{ github.job }}
env:
GH_AW_SETUP_WORKFLOW_NAME: "Repo Assist"
GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/repo-assist.lock.yml@${{ github.ref }}
GH_AW_INFO_VERSION: "1.0.80"
GH_AW_INFO_AWF_VERSION: "v0.28.9"
GH_AW_INFO_BODY_MODIFIED: "false"
GH_AW_INFO_ENGINE_ID: "copilot"
- name: Check command position
id: check_command_position
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_AW_COMMANDS: "[\"repo-assist\"]"
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_command_position.cjs'));
await main();
- name: Check team membership for command workflow
id: check_membership
if: steps.check_command_position.outputs.command_position_ok == 'true'
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_AW_REQUIRED_ROLES: "admin,maintainer,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_membership.cjs'));
await main();
- id: check
run: |
MAX_OPEN_PRS=8
if [[ "$GITHUB_EVENT_NAME" != "schedule" ]]; then exit 0; fi
COUNT=$(gh pr list --repo "$GITHUB_REPOSITORY" --state open --search 'in:title "[repo-assist]"' --json number --jq 'length')
[[ "$COUNT" -lt "$MAX_OPEN_PRS" ]]
env:
GH_TOKEN: ${{ github.token }}
push_repo_memory:
needs:
- activation
- agent
- detection
if: >
always() && (!cancelled()) && (needs.detection.result == 'success' || needs.detection.result == 'skipped') &&
needs.agent.result == 'success'
runs-on: ubuntu-slim
permissions:
contents: write
concurrency:
group: "push-repo-memory-${{ github.repository }}|memory/repo-assist"
cancel-in-progress: false
env:
GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
outputs:
patch_size_exceeded_default: ${{ steps.push_repo_memory_default.outputs.patch_size_exceeded }}
validation_error_default: ${{ steps.push_repo_memory_default.outputs.validation_error }}
validation_failed_default: ${{ steps.push_repo_memory_default.outputs.validation_failed }}
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw/actions/setup@76f6ea7c222034dcb6282848ba08935d75df1b8f # 76f6ea7c222034dcb6282848ba08935d75df1b8f
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: "Repo Assist"
GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/repo-assist.lock.yml@${{ github.ref }}
GH_AW_INFO_VERSION: "1.0.80"
GH_AW_INFO_AWF_VERSION: "v0.28.9"
GH_AW_INFO_BODY_MODIFIED: "false"
GH_AW_INFO_ENGINE_ID: "copilot"
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
sparse-checkout: .
- 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: Download repo-memory artifact (default)
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
continue-on-error: true
with:
name: repo-memory-default
path: /tmp/gh-aw/repo-memory/default
- name: Push repo-memory changes (default)
id: push_repo_memory_default
if: always()
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_TOKEN: ${{ github.token }}
GITHUB_RUN_ID: ${{ github.run_id }}
GITHUB_SERVER_URL: ${{ github.server_url }}
ARTIFACT_DIR: /tmp/gh-aw/repo-memory/default
MEMORY_ID: default
TARGET_REPO: ${{ github.repository }}
BRANCH_NAME: memory/repo-assist
MAX_FILE_SIZE: 65536
MAX_FILE_COUNT: 1
MAX_PATCH_SIZE: 65536
ALLOWED_EXTENSIONS: '[".json"]'
FORMAT_JSON: 'true'
VALIDATION_SCRIPT_B64: Y29uc3QgZmFpbCA9IG1lc3NhZ2UgPT4geyB0aHJvdyBuZXcgRXJyb3IoYG5vdGVzLmpzb246ICR7bWVzc2FnZX1gKTsgfTsKY29uc3QgZGF0YSA9IEpTT04ucGFyc2UoZnMucmVhZEZpbGVTeW5jKHBhdGguam9pbihtZW1vcnlSb290LCAibm90ZXMuanNvbiIpLCAidXRmOCIpKTsKY29uc3QgaXNPYmplY3QgPSB2YWx1ZSA9PiB2YWx1ZSAhPT0gbnVsbCAmJiB0eXBlb2YgdmFsdWUgPT09ICJvYmplY3QiICYmICFBcnJheS5pc0FycmF5KHZhbHVlKTsKY29uc3QgZXhhY3RLZXlzID0gKHZhbHVlLCBrZXlzKSA9PiBpc09iamVjdCh2YWx1ZSkgJiYgT2JqZWN0LmtleXModmFsdWUpLnNvcnQoKS5qb2luKCIsIikgPT09IFsuLi5rZXlzXS5zb3J0KCkuam9pbigiLCIpOwpjb25zdCB2YWxpZERhdGUgPSB2YWx1ZSA9PiB0eXBlb2YgdmFsdWUgPT09ICJzdHJpbmciICYmIC9eWzAtOV17NH0tWzAtOV17Mn0tWzAtOV17Mn0kLy50ZXN0KHZhbHVlKTsKY29uc3QgdmFsaWRUZXh0ID0gKHZhbHVlLCBtYXhpbXVtKSA9PiB0eXBlb2YgdmFsdWUgPT09ICJzdHJpbmciICYmIHZhbHVlLmxlbmd0aCA+IDAgJiYgdmFsdWUubGVuZ3RoIDw9IG1heGltdW07CmNvbnN0IHVuaXF1ZSA9IChlbnRyaWVzLCBrZXksIGxhYmVsKSA9PiB7CiAgY29uc3QgdmFsdWVzID0gZW50cmllcy5tYXAoa2V5KTsKICBpZiAobmV3IFNldCh2YWx1ZXMpLnNpemUgIT09IHZhbHVlcy5sZW5ndGgpIGZhaWwoYCR7bGFiZWx9IG11c3QgYmUgdW5pcXVlYCk7Cn07CgppZiAoIWV4YWN0S2V5cyhkYXRhLCBbInZlcnNpb24iLCAiY3Vyc29ycyIsICJpc3N1ZXMiLCAiZml4ZXMiLCAiY2hlY2tzIiwgImNvbXBsZXRlZF9hY3Rpb25zIiwgInByaW9yaXRpZXMiXSkpIGZhaWwoIm11c3QgY29udGFpbiBleGFjdGx5IHZlcnNpb24sIGN1cnNvcnMsIGlzc3VlcywgZml4ZXMsIGNoZWNrcywgY29tcGxldGVkX2FjdGlvbnMsIGFuZCBwcmlvcml0aWVzIik7CmlmIChkYXRhLnZlcnNpb24gIT09IDEpIGZhaWwoInZlcnNpb24gbXVzdCBiZSAxIik7CmlmICghZXhhY3RLZXlzKGRhdGEuY3Vyc29ycywgWyJsYWJlbGxpbmdfYWZ0ZXIiLCAiaW52ZXN0aWdhdGlvbl9hZnRlciJdKSkgZmFpbCgiY3Vyc29ycyBtdXN0IGNvbnRhaW4gZXhhY3RseSBsYWJlbGxpbmdfYWZ0ZXIgYW5kIGludmVzdGlnYXRpb25fYWZ0ZXIiKTsKZm9yIChjb25zdCBbbmFtZSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKGRhdGEuY3Vyc29ycykpIHsKICBpZiAodmFsdWUgIT09IG51bGwgJiYgKCFOdW1iZXIuaXNJbnRlZ2VyKHZhbHVlKSB8fCB2YWx1ZSA8IDEpKSBmYWlsKGAke25hbWV9IG11c3QgYmUgbnVsbCBvciBhIHBvc2l0aXZlIGlzc3VlIG51bWJlcmApOwp9Cgpjb25zdCBpc3N1ZVN0YXRlcyA9IG5ldyBTZXQoWyJjb21tZW50ZWQiLCAiYXdhaXRpbmdfY2xhcmlmaWNhdGlvbiIsICJyZXNvbHV0aW9uX3JlY29tbWVuZGVkIiwgImRlZmVycmVkIiwgImF3YWl0aW5nX2FwcHJvdmFsIl0pOwppZiAoIUFycmF5LmlzQXJyYXkoZGF0YS5pc3N1ZXMpIHx8IGRhdGEuaXNzdWVzLmxlbmd0aCA+IDEwMCkgZmFpbCgiaXNzdWVzIG11c3QgYmUgYW4gYXJyYXkgb2YgYXQgbW9zdCAxMDAgZW50cmllcyIpOwpmb3IgKGNvbnN0IFtpbmRleCwgZW50cnldIG9mIGRhdGEuaXNzdWVzLmVudHJpZXMoKSkgewogIGlmICghZXhhY3RLZXlzKGVudHJ5LCBbIm51bWJlciIsICJzdGF0ZSIsICJ1cGRhdGVkX2F0IiwgIm5vdGUiXSkpIGZhaWwoYGludmFsaWQgaXNzdWVzIGVudHJ5IGF0IGluZGV4ICR7aW5kZXh9YCk7CiAgaWYgKCFOdW1iZXIuaXNJbnRlZ2VyKGVudHJ5Lm51bWJlcikgfHwgZW50cnkubnVtYmVyIDwgMSkgZmFpbChgaW52YWxpZCBpc3N1ZSBudW1iZXIgYXQgaW5kZXggJHtpbmRleH1gKTsKICBpZiAoIWlzc3VlU3RhdGVzLmhhcyhlbnRyeS5zdGF0ZSkpIGZhaWwoYGludmFsaWQgaXNzdWUgc3RhdGUgYXQgaW5kZXggJHtpbmRleH1gKTsKICBpZiAoIXZhbGlkRGF0ZShlbnRyeS51cGRhdGVkX2F0KSkgZmFpbChgaW52YWxpZCBpc3N1ZSBkYXRlIGF0IGluZGV4ICR7aW5kZXh9YCk7CiAgaWYgKCF2YWxpZFRleHQoZW50cnkubm90ZSwgMzAwKSkgZmFpbChgaW52YWxpZCBpc3N1ZSBub3RlIGF0IGluZGV4ICR7aW5kZXh9YCk7Cn0KdW5pcXVlKGRhdGEuaXNzdWVzLCBlbnRyeSA9PiBlbnRyeS5udW1iZXIsICJpc3N1ZSBudW1iZXJzIik7Cgpjb25zdCBmaXhTdGF0ZXMgPSBuZXcgU2V0KFsib3BlbiIsICJtZXJnZWQiLCAiY2xvc2VkIiwgImJsb2NrZWQiXSk7CmlmICghQXJyYXkuaXNBcnJheShkYXRhLmZpeGVzKSB8fCBkYXRhLmZpeGVzLmxlbmd0aCA+IDUwKSBmYWlsKCJmaXhlcyBtdXN0IGJlIGFuIGFycmF5IG9mIGF0IG1vc3QgNTAgZW50cmllcyIpOwpmb3IgKGNvbnN0IFtpbmRleCwgZW50cnldIG9mIGRhdGEuZml4ZXMuZW50cmllcygpKSB7CiAgaWYgKCFleGFjdEtleXMoZW50cnksIFsiaXNzdWUiLCAicHIiLCAiYnJhbmNoIiwgInN0YXR1cyIsICJ1cGRhdGVkX2F0IiwgIm5vdGUiXSkpIGZhaWwoYGludmFsaWQgZml4ZXMgZW50cnkgYXQgaW5kZXggJHtpbmRleH1gKTsKICBpZiAoIU51bWJlci5pc0ludGVnZXIoZW50cnkuaXNzdWUpIHx8IGVudHJ5Lmlzc3VlIDwgMSkgZmFpbChgaW52YWxpZCBmaXggaXNzdWUgYXQgaW5kZXggJHtpbmRleH1gKTsKICBpZiAoZW50cnkucHIgIT09IG51bGwgJiYgKCFOdW1iZXIuaXNJbnRlZ2VyKGVudHJ5LnByKSB8fCBlbnRyeS5wciA8IDEpKSBmYWlsKGBpbnZhbGlkIGZpeCBQUiBhdCBpbmRleCAke2luZGV4fWApOwogIGlmIChlbnRyeS5icmFuY2ggIT09IG51bGwgJiYgIXZhbGlkVGV4dChlbnRyeS5icmFuY2gsIDEyMCkpIGZhaWwoYGludmFsaWQgZml4IGJyYW5jaCBhdCBpbmRleCAke2luZGV4fWApOwogIGlmICghZml4U3RhdGVzLmhhcyhlbnRyeS5zdGF0dXMpKSBmYWlsKGBpbnZhbGlkIGZpeCBzdGF0dXMgYXQgaW5kZXggJHtpbmRleH1gKTsKICBpZiAoIXZhbGlkRGF0ZShlbnRyeS51cGRhdGVkX2F0KSkgZmFpbChgaW52YWxpZCBmaXggZGF0ZSBhdCBpbmRleCAke2luZGV4fWApOwogIGlmICghdmFsaWRUZXh0KGVudHJ5Lm5vdGUsIDMwMCkpIGZhaWwoYGludmFsaWQgZml4IG5vdGUgYXQgaW5kZXggJHtpbmRleH1gKTsKfQp1bmlxdWUoZGF0YS5maXhlcywgZW50cnkgPT4gZW50cnkuaXNzdWUsICJmaXggaXNzdWUgbnVtYmVycyIpOwp1bmlxdWUoZGF0YS5maXhlcy5maWx0ZXIoZW50cnkgPT4gZW50cnkucHIgIT09IG51bGwpLCBlbnRyeSA9PiBlbnRyeS5wciwgImZpeCBQUiBudW1iZXJzIik7Cgpjb25zdCBjaGVja0FyZWFzID0gbmV3IFNldChbImRlcGVuZGVuY2llcyIsICJjaSIsICJ0b29saW5nIiwgImJ1aWxkIiwgImNvZGUiLCAiZG9jcyIsICJxYSIsICJoeWdpZW5lIiwgInBlcmZvcm1hbmNlIiwgInRlc3RzIiwgInJlbGVhc2UiLCAicmVwb19hc3Npc3RfcHJzIl0pOwppZiAoIUFycmF5LmlzQXJyYXkoZGF0YS5jaGVja3MpIHx8IGRhdGEuY2hlY2tzLmxlbmd0aCA+IGNoZWNrQXJlYXMuc2l6ZSkgZmFpbCgiY2hlY2tzIG11c3QgY29udGFpbiBhdCBtb3N0IG9uZSBlbnRyeSBwZXIgYXJlYSIpOwpmb3IgKGNvbnN0IFtpbmRleCwgZW50cnldIG9mIGRhdGEuY2hlY2tzLmVudHJpZXMoKSkgewogIGlmICghZXhhY3RLZXlzKGVudHJ5LCBbImFyZWEiLCAiY2hlY2tlZF9hdCIsICJyZXN1bHQiLCAiZm9sbG93X3VwIl0pKSBmYWlsKGBpbnZhbGlkIGNoZWNrcyBlbnRyeSBhdCBpbmRleCAke2luZGV4fWApOwogIGlmICghY2hlY2tBcmVhcy5oYXMoZW50cnkuYXJlYSkpIGZhaWwoYGludmFsaWQgY2hlY2sgYXJlYSBhdCBpbmRleCAke2luZGV4fWApOwogIGlmICghdmFsaWREYXRlKGVudHJ5LmNoZWNrZWRfYXQpKSBmYWlsKGBpbnZhbGlkIGNoZWNrIGRhdGUgYXQgaW5kZXggJHtpbmRleH1gKTsKICBpZiAoIXZhbGlkVGV4dChlbnRyeS5yZXN1bHQsIDMwMCkpIGZhaWwoYGludmFsaWQgY2hlY2sgcmVzdWx0IGF0IGluZGV4ICR7aW5kZXh9YCk7CiAgaWYgKGVudHJ5LmZvbGxvd191cCAhPT0gbnVsbCAmJiAhdmFsaWRUZXh0KGVudHJ5LmZvbGxvd191cCwgMzAwKSkgZmFpbChgaW52YWxpZCBjaGVjayBmb2xsb3dfdXAgYXQgaW5kZXggJHtpbmRleH1gKTsKfQp1bmlxdWUoZGF0YS5jaGVja3MsIGVudHJ5ID0+IGVudHJ5LmFyZWEsICJjaGVjayBhcmVhcyIpOwoKaWYgKCFBcnJheS5pc0FycmF5KGRhdGEuY29tcGxldGVkX2FjdGlvbnMpIHx8IGRhdGEuY29tcGxldGVkX2FjdGlvbnMubGVuZ3RoID4gMTAwKSBmYWlsKCJjb21wbGV0ZWRfYWN0aW9ucyBtdXN0IGJlIGFuIGFycmF5IG9mIGF0IG1vc3QgMTAwIGVudHJpZXMiKTsKZm9yIChjb25zdCBbaW5kZXgsIGVudHJ5XSBvZiBkYXRhLmNvbXBsZXRlZF9hY3Rpb25zLmVudHJpZXMoKSkgewogIGlmICghZXhhY3RLZXlzKGVudHJ5LCBbImtleSIsICJjb21wbGV0ZWRfYXQiXSkpIGZhaWwoYGludmFsaWQgY29tcGxldGVkX2FjdGlvbnMgZW50cnkgYXQgaW5kZXggJHtpbmRleH1gKTsKICBpZiAoIXZhbGlkVGV4dChlbnRyeS5rZXksIDEwMCkpIGZhaWwoYGludmFsaWQgY29tcGxldGVkIGFjdGlvbiBrZXkgYXQgaW5kZXggJHtpbmRleH1gKTsKICBpZiAoIXZhbGlkRGF0ZShlbnRyeS5jb21wbGV0ZWRfYXQpKSBmYWlsKGBpbnZhbGlkIGNvbXBsZXRlZCBhY3Rpb24gZGF0ZSBhdCBpbmRleCAke2luZGV4fWApOwp9CnVuaXF1ZShkYXRhLmNvbXBsZXRlZF9hY3Rpb25zLCBlbnRyeSA9PiBlbnRyeS5rZXksICJjb21wbGV0ZWQgYWN0aW9uIGtleXMiKTsKCmlmICghQXJyYXkuaXNBcnJheShkYXRhLnByaW9yaXRpZXMpIHx8IGRhdGEucHJpb3JpdGllcy5sZW5ndGggPiAyMCkgZmFpbCgicHJpb3JpdGllcyBtdXN0IGJlIGFuIGFycmF5IG9mIGF0IG1vc3QgMjAgZW50cmllcyIpOwpmb3IgKGNvbnN0IFtpbmRleCwgZW50cnldIG9mIGRhdGEucHJpb3JpdGllcy5lbnRyaWVzKCkpIHsKICBpZiAoIWV4YWN0S2V5cyhlbnRyeSwgWyJ0YXNrIiwgIml0ZW0iLCAibm90ZSJdKSkgZmFpbChgaW52YWxpZCBwcmlvcml0aWVzIGVudHJ5IGF0IGluZGV4ICR7aW5kZXh9YCk7CiAgaWYgKCFOdW1iZXIuaXNJbnRlZ2VyKGVudHJ5LnRhc2spIHx8IGVudHJ5LnRhc2sgPCAxIHx8IGVudHJ5LnRhc2sgPiAxMCkgZmFpbChgaW52YWxpZCBwcmlvcml0eSB0YXNrIGF0IGluZGV4ICR7aW5kZXh9YCk7CiAgaWYgKCF2YWxpZFRleHQoZW50cnkuaXRlbSwgMTAwKSkgZmFpbChgaW52YWxpZCBwcmlvcml0eSBpdGVtIGF0IGluZGV4ICR7aW5kZXh9YCk7CiAgaWYgKCF2YWxpZFRleHQoZW50cnkubm90ZSwgMzAwKSkgZmFpbChgaW52YWxpZCBwcmlvcml0eSBub3RlIGF0IGluZGV4ICR7aW5kZXh9YCk7Cn0KdW5pcXVlKGRhdGEucHJpb3JpdGllcywgZW50cnkgPT4gYCR7ZW50cnkudGFza306JHtlbnRyeS5pdGVtfWAsICJwcmlvcml0eSB0YXNrL2l0ZW0gcGFpcnMiKTsKY29uc29sZS5sb2coInJlcG8tYXNzaXN0IG5vdGVzLmpzb24gY29uZm9ybXMgdG8gc2NoZW1hIik7Cg==
VALIDATION_TIMEOUT_SECONDS: 60
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, 'push_repo_memory.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: write
issues: write
pull-requests: write
timeout-minutes: 45
env:
GH_AW_AGENT_AIC: ${{ needs.agent.outputs.aic }}
GH_AW_AIC: ${{ needs.agent.outputs.aic }}
GH_AW_AMBIENT_CONTEXT: ${{ needs.agent.outputs.ambient_context }}
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/repo-assist"
GH_AW_COMMANDS: "[\"repo-assist\"]"
GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }}
GH_AW_ENGINE_ID: "copilot"
GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }}
GH_AW_RUNTIME_FEATURES: ${{ vars.GH_AW_RUNTIME_FEATURES }}
GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e Generated by 🌈 {workflow_name}, see [workflow run]({run_url}). [Learn more](https://github.com/githubnext/agentics/blob/main/docs/repo-assist.md).\",\"runStarted\":\"{workflow_name} is processing {event_type}, see [workflow run]({run_url})...\",\"runSuccess\":\"✓ {workflow_name} completed successfully, see [workflow run]({run_url}).\",\"runFailure\":\"✗ {workflow_name} encountered {status}, see [workflow run]({run_url}).\"}"
GH_AW_THREAT_DETECTION_AIC: ${{ needs.detection.outputs.aic }}
GH_AW_WORKFLOW_ID: "repo-assist"
GH_AW_WORKFLOW_NAME: "Repo Assist"
GH_AW_WORKFLOW_SOURCE: "githubnext/agentics/workflows/repo-assist.md@ae8d551f07c7ed7619f8c58c7bb4c3ac89395d38"
GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/githubnext/agentics/blob/ae8d551f07c7ed7619f8c58c7bb4c3ac89395d38/workflows/repo-assist.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 }}
comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }}
comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }}
create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
created_issue_number: ${{ steps.process_safe_outputs.outputs.created_issue_number }}
created_issue_url: ${{ steps.process_safe_outputs.outputs.created_issue_url }}
created_pr_number: ${{ steps.process_safe_outputs.outputs.created_pr_number }}
created_pr_url: ${{ steps.process_safe_outputs.outputs.created_pr_url }}
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 }}
push_commit_sha: ${{ steps.process_safe_outputs.outputs.push_commit_sha }}
push_commit_url: ${{ steps.process_safe_outputs.outputs.push_commit_url }}
steps:
- name: Setup Scripts
id: setup
uses: github/gh-aw/actions/setup@76f6ea7c222034dcb6282848ba08935d75df1b8f # 76f6ea7c222034dcb6282848ba08935d75df1b8f
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: "Repo Assist"
GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/repo-assist.lock.yml@${{ github.ref }}
GH_AW_INFO_VERSION: "1.0.80"
GH_AW_INFO_AWF_VERSION: "v0.28.9"
GH_AW_INFO_BODY_MODIFIED: "false"
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: Download patch artifact
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: agent
path: /tmp/gh-aw/
- name: Checkout repository
if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') || (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: true
fetch-depth: 0
token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- name: Fetch additional refs
if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') || (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')
env:
GH_AW_FETCH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: |
header=$(printf "x-access-token:%s" "${GH_AW_FETCH_TOKEN}" | base64 -w 0)
git -c "http.extraheader=Authorization: Basic ${header}" fetch origin '+refs/heads/*:refs/remotes/origin/*'
- name: Configure Git credentials
if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request') || (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'push_to_pull_request_branch')
env:
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_SERVER_URL: ${{ github.server_url }}
GIT_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_git_credentials.sh"
- name: Configure GH_HOST for enterprise compatibility
id: ghes-host-config
shell: bash
run: | # zizmor: ignore[github-env] - GITHUB_SERVER_URL is set by GitHub Actions, not user input.
# Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct
# GitHub instance (GHES/GHEC). On github.com this is a harmless no-op.
GH_HOST="${GITHUB_SERVER_URL#https://}"
GH_HOST="${GH_HOST#http://}"
echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
- name: Process Safe Outputs
id: process_safe_outputs
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }}
GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.gradle-enterprise.cloud,*.pythonhosted.org,*.vsblob.vsassets.io,adoptium.net,anaconda.org,api.adoptium.net,api.foojay.io,api.npms.io,api.nuget.org,api.snapcraft.io,appveyorcidata.blob.core.windows.net,archive.apache.org,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,binstar.org,bootstrap.pypa.io,builds.dotnet.microsoft.com,bun.sh,cdn.azul.com,cdn.jsdelivr.net,central.sonatype.com,ci.appveyor.com,ci.dot.net,codeload.github.com,conda.anaconda.org,conda.binstar.org,crates.io,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,dc.services.visualstudio.com,deb.nodesource.com,deno.land,develocity.apache.org,dist.nuget.org,dl.google.com,dlcdn.apache.org,docs.github.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,download.eclipse.org,download.java.net,download.oracle.com,downloads.gradle-dn.com,esm.sh,files.pythonhosted.org,ge.spockframework.org,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,googleapis.deno.dev,googlechromelabs.github.io,gradle.org,index.crates.io,jcenter.bintray.com,jdk.java.net,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,maven-central.storage-download.googleapis.com,maven.apache.org,maven.google.com,maven.oracle.com,maven.pkg.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,patchdiff.githubusercontent.com,pip.pypa.io,pkgs.dev.azure.com,plugins-artifacts.gradle.org,plugins.gradle.org,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.gradle.org,repo.grails.org,repo.maven.apache.org,repo.spring.io,repo.yarnpkg.com,repo1.maven.org,repository.apache.org,s.symcb.com,s.symcd.com,scans-in.gradle.com,security.ubuntu.com,services.gradle.org,sh.rustup.rs,skimdb.npmjs.com,static.crates.io,static.rust-lang.org,storage.googleapis.com,telemetry.vercel.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,www.java.com,www.microsoft.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":10,\"target\":\"*\"},\"add_labels\":{\"allowed\":[\"bug\",\"enhancement\",\"help wanted\",\"good first issue\",\"spam\",\"off topic\",\"documentation\",\"question\",\"duplicate\",\"wontfix\",\"needs triage\",\"needs investigation\",\"breaking change\",\"performance\",\"security\",\"refactor\"],\"max\":30,\"target\":\"*\"},\"create_issue\":{\"labels\":[\"automation\",\"repo-assist\"],\"max\":4,\"title_prefix\":\"[repo-assist] \"},\"create_pull_request\":{\"draft\":true,\"labels\":[\"automation\",\"repo-assist\"],\"max\":4,\"max_patch_files\":100,\"max_patch_size\":4096,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"CONTRIBUTING.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\"],\"protected_files_policy\":\"request_review\",\"title_prefix\":\"[repo-assist] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max\":4,\"max_patch_size\":4096,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"CONTRIBUTING.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\"],\"protected_files_policy\":\"allowed\",\"target\":\"*\",\"title_prefix\":\"[repo-assist] \"},\"remove_labels\":{\"allowed\":[\"bug\",\"enhancement\",\"help wanted\",\"good first issue\",\"spam\",\"off topic\",\"documentation\",\"question\",\"duplicate\",\"wontfix\",\"needs triage\",\"needs investigation\",\"breaking change\",\"performance\",\"security\",\"refactor\"],\"max\":5,\"target\":\"*\"},\"report_incomplete\":{},\"update_issue\":{\"allow_body\":true,\"max\":1,\"required_title_prefix\":\"[repo-assist] \",\"target\":\"*\"}}"
GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }}
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